Jar信息对比
CyclicBarrier和CountDownLatch 都位于java.util.concurrent 这个包下
| CountDownLatch | CyclicBarrier |
|---|---|
| 减计数方式 | 加计数方式 |
| 计算为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控制员工签署的文件是否都下发完毕,完毕在执行下个文件签署,实现有序的完成签署的任务
重要方法:
-
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
-
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
-
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); }