CountDownLatch /CyclicBarrier /Semaphore

Updated on with 0 views and 0 comments

CountDownLatch

CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信作用。

常用方法:

//实例化一个倒计数器,count指定计数个数
CountDownLatch(int count) 
// 计数减一
countDown()
//等待,当计数减到0时,所有线程并行执行
await()
 public static void main(String[] args) {
        /**
         * 这样的一个场景
         * 我在高中时 是班长,每天晚自习我都要最后走并锁门(很苦逼)
         */
        String[] students = {"张三", "李四", "王二", "刘五"};

        for (int i = 0; i < students.length; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 上完自习,离开教室。");
            }, students[i]).start();
        }
        System.out.println(Thread.currentThread().getName() + "班长(我)离开教室,把门锁了。");

        /**
         * 代码从上到下顺序执行 over,完美。(但事实如下,刘五第二天退学了)
         * 
         * 结果(打印):
         * 张三 上完自习,离开教室。
         * 李四 上完自习,离开教室。
         * 王二 上完自习,离开教室。
         * main班长(我)离开教室,把门锁了。
         * 刘五 上完自习,离开教室。
         */
    }

痛定思痛,为了为班级创造良好的自习环境和保证同学的“安全”,CountDownLatch来了。

 public static void main(String[] args) throws InterruptedException {
        /**
         * 这样的一个场景
         * 我在高中时 是班长,每天晚自习我都要最后走并锁门(很苦逼)
         */
        String[] students = {"张三", "李四", "王二", "刘五"};
        CountDownLatch countDownLatch = new CountDownLatch(students.length);


        for (int i = 0; i < students.length; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 上完自习,离开教室。");
                countDownLatch.countDown();
            }, students[i]).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "班长(我)离开教室,把门锁了。");

       
        /**
         * over,完美(班长手里拿着countDownLatch,实时掌握班内同学情况,一旦有人离开教室countDownLatch--,为0关门,ok)。
         * 
         * 张三 上完自习,离开教室。
         * 王二 上完自习,离开教室。
         * 李四 上完自习,离开教室。
         * 刘五 上完自习,离开教室。
         * main班长(我)离开教室,把门锁了。
         */
    }

使用场景

上面的案例就是CountDownLatch的一个非常典型的应用场景是:一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

CyclicBarrier

 /**
         * 中国吃饭传统为 人必须到齐后才能动筷子吃饭
         * 我家里有5口人
         */
        String[] familys = {"爸爸","妈妈","佳佳","liqitian3344", "俊杰"};

        CyclicBarrier cyclicBarrier = new CyclicBarrier(familys.length, () -> {
            System.out.println("到齐开吃...");
        });

        for (int i = 0; i < familys.length; i++) {

            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName()+"入座...");

                    //入座等待
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(familys[i])).start();
        }
        /**
         * 结果(打印):
         * 爸爸入座...
         * 佳佳入座...
         * 妈妈入座...
         * liqitian3344入座...
         * 俊杰入座...
         * 到齐开吃...
         */

常用方法:

//parties 是参与线程的个数
//构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)

//线程调用 await() 表示自己已经到达栅栏
//BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

使用场景

CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如现在需要计算10个人12个月内的工资详细,可以将线程分为10个,分别计算每个人的工资,最后,再用barrierAction将这些线程的计算结果进行整合,得出最后结果。

CountDownLatch与CyclicBarrier区别

  1. CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
  2. CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
  3. CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
  4. CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
  5. CountDownLatch和CyclicBarrier都有让多个线程等待同步然后再开始下一步动作的意思,但是CountDownLatch的下一步的动作实施者是主线程,具有不可重复性;而CyclicBarrier的下一步动作实施者还是“其他线程”本身,具有往复多次实施动作的特点。

Semaphore

   /**
         * 在上海这边节假日吃饭就很烦,基本都是满员,经常被迫排队
         * 比如"XX餐厅" 座位一共5个(一个座位至多坐一个人)
         * 我们来模拟7个人抢5个座位,第一波疯抢后,剩下1个人继续等待,后面走一个人,进去一个人。
         */
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i <7 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+",抢到座位,开始进餐...");
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println(Thread.currentThread().getName()+",吃了5秒钟,离开了");
                    semaphore.release();
                }
            },"代号"+i+"顾客").start();
        }
        /**
         * 结果(打印):
         * 代号0顾客,抢到座位,开始进餐...
         * 代号1顾客,抢到座位,开始进餐...
         * 代号2顾客,抢到座位,开始进餐...
         * 代号3顾客,抢到座位,开始进餐...
         * 代号4顾客,抢到座位,开始进餐...
         * 代号1顾客,吃了5秒钟,离开了
         * 代号0顾客,吃了5秒钟,离开了
         * 代号2顾客,吃了5秒钟,离开了
         * 代号3顾客,吃了5秒钟,离开了
         * 代号6顾客,抢到座位,开始进餐...
         * 代号5顾客,抢到座位,开始进餐...
         * 代号4顾客,吃了5秒钟,离开了
         * 代号5顾客,吃了5秒钟,离开了
         * 代号6顾客,吃了5秒钟,离开了
         */

 Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。就这一点而言。

Semaphore计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。信号量还可以用来实现某种资源池,或者对容器施加边界。

上面的例子就是Semaphore经典的使用场景,其实还有点更好的例子:
每天早上,大家都热衷于带薪上厕所,但是公司厕所一共只有10个坑位。。那么只能同时10个人用着,后面来的人都得等着(阻塞),如果走了2个人,那么又可以进去2个人。这里面就是Semaphore的应用场景,争夺有限的资源。除了上面这些例子,我们在工作中经常用它管理数据库连接或者保护其它受限资源的并发使用(是不是感觉和 RateLimiterxxxx)。

常用方法:

//从此信号量获取一个许可,在提供一个许可前一直将线程阻塞。
  void acquire():
//释放一个许可,将其返回给信号量。
  void release():
//返回此信号量中当前可用的许可数。
  int availablePermits():
//查询是否有线程正在等待获取。
  boolean hasQueuedThreads():

原理待续~~


标题:CountDownLatch /CyclicBarrier /Semaphore
作者:liqitian3344
地址:https://liqitian.com/articles/2020/07/07/1594130131709.html