您的位置:首页 > 其它

线程的并发工具类CountDownLatch和CyclicBarrier

2020-07-14 06:31 239 查看

1.CountDownLatch简介

1.CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成。
2.闭锁,CountDownLatch 这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动 所有的框架服务之后再执行。
3.CountDownLatch 是通过一个计数器来实现的,计数器的初始值为初始任务 的数量。每当完成了一个任务后,计数器的值就会减 1 (CountDownLatch.countDown()方法)。当计数器值到达 0 时,它表示所有的已 经完成了任务,然后在闭锁上等待 CountDownLatch.await()方法的线程就可以恢 复执行任务。

2.CountDownLatch相关api

//返回当前的计数count值
public long getCount() {
return sync.getCount();
}

/*调用此方法后,会减少计数count的值。
递减后如果为0,则会释放所有等待的线程*/
public void countDown()

/*调用CountDownLatch对象的await方法后。
会让当前线程阻塞,直到计数count递减至0。*/
public void await()
throws InterruptedException

/*同时await还提供一个带参数和返回值的方法。
如果计数count正常递减,返回0后,await方法会返回true并继续执行后续逻辑。
或是,尚未递减到0,而到达了指定的时间间隔后,方法返回false。
如果时间小于等于0,则此方法不执行等待。*/
public boolean await(long timeout,
TimeUnit unit)
throws InterruptedException

注意事项:
1.在定义扣减数时,不需要和初始线程数保持一致,但一般会大于等于初始线程数,扣减数和代码中调用countdown()的次数保持一致;
2.在调用await()方法后,线程会阻塞,以下两种情况会脱离阻塞状态:
(1)、调用countDown()方法,将计数count递减至0。
(2)、当前线程被其他线程打断。
3.countDownLatch.countDown();
这一句话尽量写在finally中,或是保证此行代码前的逻辑正常运行,因为在一些情况下,出现异常会导致无法减一,然后出现死锁。
CountDownLatch 是一次性使用的,当计数值在构造函数中初始化后,就不能再对其设置任何值,当 CountDownLatch 使用完毕,也不能再次被使用。

3.CountDownLatch应用场景

(1)实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
(2)开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。
(3)死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁

4.CyclicBarrier简介

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做 的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一 个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

5.CyclicBarrier的api

//该参数表示屏障拦截的线程数量
public CyclicBarrier(int parties) {
this(parties, null);
}

//在线程全部到达屏障时,需要先执行barrierAction才可让所有阻塞的线程继续运行
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}

//每个线程通过调用该方法告诉CyclicBarrier我已经到达屏障,然后进行阻塞,等待所有线程都到达屏障才同时释放
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}

//使CyclicBarrier回到初始状态,所以称之为可循环屏障
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier();   // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}

6.CyclicBarrier的应用场景

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

4.两者的区别

1.CountDownLatch 的计数器只能使用一次,而 CyclicBarrier 的计数器可以反复 使用。
2.CountDownLatch.await 一般阻塞工作线程,所有的进行预备工作的线程执行 countDown,而 CyclicBarrier 通过工作线程调用 await 从而自行阻塞,直到所有工 作线程达到指定屏障,再大家一起往下走。
3.在控制多个线程同时运行上,CountDownLatch 可以不限线程数量,而 CyclicBarrier 是固定线程数。
同时,CyclicBarrier 还可以提供一个 barrierAction,合并多线程计算结果。
4.CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以5.CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
6.CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
7.CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐