CountDownLatch、CyclicBarrier对比

1,087 阅读3分钟

Jar信息对比

CyclicBarrier和CountDownLatch 都位于java.util.concurrent 这个包下

CountDownLatchCyclicBarrier
减计数方式加计数方式
计算为0时释放所有等待的线程计数达到指定值时释放所有等待线程
计数为0时,无法重置计数达到指定值时,计数置为0重新开始
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用可重复利用

实现逻辑对比

1、CountDownLatch countDown方法不阻塞,await方法阻塞 内部类继承自AQS框架,实现share相关方法 不可重复使用,一般用于某些线程等一堆线程操作

2、CyclicBarrier 循环栅栏,可循环使用,一般用于多个线程互相等待 也可实现CountDownLatch功能,在初始化的时候多加一个Runnable参数 1.8使用ReentrantLock和condition实现,由于有多个共享变量需要保证原子性 generation和count操作需要保证原子性;

线程间通信使用的是Condition,Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用。

3、Semaphore 信号量Java实现 内部类继承自AQS框架,实现share相关方法 与锁类似,只不过不记录获取的线程

4、Exchanger 需两个线程访问,如果只有一个线程,会阻塞等待另一个线程到来 内部实现:CAS+park

代码剖析

线程在countDown()之后,会继续执行自己的任务,而CyclicBarrier会在所有线程任务结束之后,才会进行后续任务

public CyclicBarrier(int parties, Runnable barrierAction) {

}

public CyclicBarrier(int parties) {

}

参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

CyclicBarrier中最重要的方法就是await方法

1、public int await() throws InterruptedException, BrokenBarrierException { };//挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;

2、∂public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };//让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务

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

项目应用

第一。CountDownLatch项目应用:

下发多个签署的文件信息,通过CountDownLatch控制员工签署的文件是否都下发完毕,完毕在执行下个文件签署,实现有序的完成签署的任务

重要方法:

  1. public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行

  2. public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行

  3. public void countDown() { }; //将count值减1

    CountDownLatch latch = new CountDownLatch(fileList.size());
    RedisUtil.setKey(RedisUtil.COUNTDOWNLUNCAH_PREFIX + backSignDTO.getContractId(), fileList.size());
    ​
    for (ThirdFile thirdFile : fileList) {
        ExecutorUtils.execute(new SendCertificate(backSignDTO.getContractId(), documents, thirdFile, latch));
    }
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    ​
    if (RedisUtil.getKey(backSignDTO.getContractId()).equals(new Long(0))) {
        logger.error("内部合同id:【" + backSignDTO.getContractId() + "】发起完毕");
    } else {
        logger.error("内部合同id:【" + backSignDTO.getContractId() + "】发起失败");
        BusinessException.throwMessageWithCode(CodeEnum.CONTRACT_CREATE_ERROR);
    }