我们的一位培训师在解释CountDownLatch和CyclicBarrier的区别时提供了一个示例。
CountDownLatch
:假设有一块石头需要10个人才能举起,因此你需要等待所有10个人到齐,然后才能举起这块石头。
CyclicBarrier
:如果你要去野餐,并且需要先在某个公共地点集合,然后大家一起开始旅程。
如果有人同意这些评论,请给我一些细节。
我已经阅读了这些类的sun API。但是我需要更多的解释。
我们的一位培训师在解释CountDownLatch和CyclicBarrier的区别时提供了一个示例。
CountDownLatch
:假设有一块石头需要10个人才能举起,因此你需要等待所有10个人到齐,然后才能举起这块石头。
CyclicBarrier
:如果你要去野餐,并且需要先在某个公共地点集合,然后大家一起开始旅程。
如果有人同意这些评论,请给我一些细节。
我已经阅读了这些类的sun API。但是我需要更多的解释。
在一个假想的剧院中:
这里的一个人是一个线程,一个戏剧是一个资源。
关键差别在于 CountDownLatch
将线程分为等待者和到达者,而使用 CyclicBarrier
的所有线程都扮演两个角色。
你的门闩示例说明了全部十个人必须一起等待举起石头。这并非如此。更好的现实示例是考试监考员耐心地等待每个学生交卷。学生完成考试后就不再等待,可以离开。最后一个学生交卷(或时间到),监考员停止等待并带着试卷离开。
await
方法的线程会等待CountDownLatch计数器归零,调用countDown
方法的线程不会被阻塞。 - David Harkness真实世界的例子
我发现所有答案都缺少一个真实的例子,就是这些类在软件领域中如何使用。
CountDownLatch 一个多线程下载管理器。 下载管理器将启动多个线程同时下载文件的每个部分(前提是服务器支持多线程下载)。这里,每个线程将调用已实例化闩锁的倒计时方法。所有线程执行完成后,与倒计时闩锁相关联的线程将把不同部分中找到的数据整合成一个文件。
CyclicBarrier 与上面的情况相同,但假设文件是从P2P下载的。同样地有多个线程下载各个部分。但是这里,假设您希望在特定时间间隔后对下载的部分进行完整性检查。这时循环屏障起着重要作用。每个时间间隔后,每个线程都会等待在屏障处,以便与循环屏障相关联的线程可以进行完整性检查。由于循环屏障,可以多次进行完整性检查。
如果有任何不当之处,请指出。
使用场景1:假设您将一个大任务拆分成10个小任务,每个任务都是一个线程。在完成作业之前,您必须等待那些线程中的10个任务全部结束。
因此,主要的作业启动线程初始化了一个CountDownLatch计数器,用于计算使用的线程数量,它将任务分配给线程并等待使用await方法时latch计数降为零。每个执行者线程将在其任务结束时调用countDown。最后,当所有线程都已完成时,主线程将被唤醒,从而认为整个作业已经完成。这种情况下使用了CountDownLatch javadoc中描述的doneSignal标志。
使用场景2:假设您将一个大任务拆分成n * m个任务,分布在n个线程上。m对应于矩阵行,您需要计算每一行的总数。在这种情况下,必须在每个任务结束后同步线程,以便处理该行的总数。在这种情况下,使用初始化为线程数n的CyclicBarrier来等待每行计算的结束(实际上为m次)。
为了比较两者,CountDownLatch只能被使用1次,而CyclicBarrier可以根据算法需要为一组线程提供多个同步点。
CountDownLatch: 如果我们想让所有线程都执行
某些操作并执行倒计时
以便等待中的其他线程可以继续进行,我们可以使用倒计时锁。在这种情况下,实际执行倒计时的所有先前线程都可以继续进行,但不能保证latch.countdown()之后处理的行将在等待其他线程到达latch.countdown()之后进行,但保证其他等待的线程只有在latch.await()到达零之后才会继续进行。
CyclicBarrier: 如果我们想让所有线程都
在一个公共点上等待 + 执行某些操作
(每个await调用将减少线程继续执行的等待时间)
可以通过一次调用latch.countdown()后由所有线程调用latch.await()来使用CountDownLatch实现CyclicBarrier的功能。
但是,您无法重置/重复使用倒计时锁。
我使用CyclicBarrier的最佳示例是初始化多个缓存(由多个线程加热),然后启动进一步处理,并且我希望在同步中重新初始化其他缓存。
理论区别:
在CountDownLatch中,主线程等待其他线程完成执行。 在CyclicBarrier中,工作线程等待彼此完成执行。
一旦计数达到零且闩锁打开,您无法重复使用相同的CountDownLatch实例。另一方面,可以通过重置障碍来重复使用CyclicBarrier,一旦障碍被打破。
现实生活示例:--
CountDownLatch:考虑一个IT行业场景,经理将模块分配给开发团队(A和B),并希望只有当两个团队都完成任务时才将其分配给QA团队进行测试。
这里经理线程充当主线程,开发团队充当工作线程。经理线程等待开发团队线程完成任务。
CyclicBarrier:考虑相同的IT行业场景,经理将模块分配给开发团队(A和B)。他休假了,并要求两个团队互相等待对方完成各自的任务,一旦两个团队都完成了任务,就将其分配给QA团队进行测试。
这里经理线程充当主线程,开发团队充当工作线程。开发团队线程在完成任务后等待其他开发团队线程。
CyclicBarrier
可以重复使用,所以它更像是一个赛车旅行,在继续下一段旅程之前,大家都会在一个路标处相遇。CountDownLatch
的例子:[https://dev59.com/3VDTa4cB1Zd3GeqPGRYV#3588523]。 - trashgod对于CyclicBarrier,我能想到一个实时的例子:假设有一群旅游者在一辆小巴上。一天内需要参观多个景点。司机知道旅游者的数量。当到达第一个景点时,所有旅游者下车并在不同的时间返回;然而,小巴和旅游者必须等待直到所有旅游者都回来。一旦所有人都回来,司机才会前往下一个景点,重复相同的过程。在这里,CyclicBarrier初始化为旅游者的数量。每个旅游者就像一个线程,在返回后,他们调用CyclicBarrier await(),以便等待其他旅游者都回来。请让我知道你的想法。
我们有一条过山车,有一个等待队列。在任何时候,只有一个人可以进入过山车车厢,并坐下。每个车厢都有9个座位(3x3座位),可以容纳9个人。当其中3个座位被占满后,这3个座位的安全杆便会关闭。当所有车厢(我们总共有3个车厢,所以有3x9个座位,即27个座位)都被占满时,过山车就会出发。
CyclicBarrier可用于为每组3个座位关闭安全杆。 在CyclicBarrier中,子线程将等待所有3个座位被占满,然后才能继续并单独关闭每个线程的安全杆。由于是循环的,因此可以在每组3个座位上重复使用。
CountDownLatch可以用于在所有座位被占满后启动过山车。一种类似主线程的线程将等待所有座位被占满。
package Concurrency;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchVsCyclicBarrier
{
private final ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private final Integer numberOfSeats = 27;
private final Integer numberOfSeatsInOneRow = 3;
private final CountDownLatch latch = new CountDownLatch(numberOfSeats);
private final CyclicBarrier barrier = new CyclicBarrier(numberOfSeatsInOneRow);
public void run() throws Exception
{
for (int person = 1; person < 28; person++) {
int finalPerson = person;
es.submit(new Runnable() {
@Override
public void run() {
System.out.println("Waiting for person " + finalPerson + " to take a seat");
try { Thread.sleep(1500); } catch(InterruptedException ignored) {}
System.out.println("Waiting to close safety bar for person " + finalPerson);
try { barrier.await(); } catch(Exception ignored) {}
System.out.println("Closing the safety bar for person " + finalPerson);
latch.countDown();
}
});
Thread.sleep(100);
}
latch.await(); // Wait for all 27 seats to be taken
System.out.println("Start the rollercoaster");
es.shutdown();
}
public static void main(String[] args) throws Exception
{
new CountDownLatchVsCyclicBarrier().run();
}
}