我正在学习Java中的同步。我无法理解CountDownLatch的确切机制。
CountDownLatch '是否按照声明时给出的线程数来倒计时'(等待完成线程数)?
以下是我试图理解的代码:
public class LatchExample implements Runnable {
private CountDownLatch latch;
private int id;
public LatchExample(int id, CountDownLatch latch){
this.id=id;
this.latch = latch;
}
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 7; i++) {
executor.submit(new LatchExample(i,latch));
}
try {
latch.await();
System.out.println("all process completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
}
@Override
public void run() {
System.out.println("Starting: "+id);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
}
在上面的示例中:
ExecutorService(来自线程池)生成7个线程。我的理解是,锁存器应该等待完成6个线程(从0到5),如下所定义:
CountDownLatch latch = new CountDownLatch(5);
但我得到的输出并不是每次都是不变的。有时它等待6个线程完成,有时等待7个例如:
Starting: 1
Starting: 0
Starting: 2
Starting: 3
Starting: 5
Starting: 4
Starting: 6
all process completed
以下是交替时间的输出:
Starting: 0
Starting: 2
Starting: 1
Starting: 4
Starting: 5
Starting: 3
all process completed
Starting: 6
编辑:CountDownLatch理想情况下应该是countDown,直到5个任务通过锁存器。这里显示为6或7。
如果我希望代码在'所有流程完成'之前始终只显示5个任务,那么代码的修复方法是什么?
答案 0 :(得分:4)
您的闩锁需要五次countDown()
来电才能达到零并让await
返回。但是,写入执行程序的任务的方式,将开始运行比释放锁存器所需的更多任务。查看代码所在位置的最有用的方法是逐步完成运行,如下所示:
Starting: n
打印了三次,其中n
的范围介于0和2之间,但是按任意顺序排列; Starting: n
打印了三次n
,范围在3到5之间,按任意顺序排列; all processes completed
几乎与Starting: 6
同时打印,按任意顺序排列。现在,我不太清楚你对代码的期望是什么,但我希望上面的推理方法可以帮助你把它带到一个行为符合你期望的状态。
答案 1 :(得分:1)
我认为了解countDownLatch背后机制的最佳方法是进行类比,所以我的2美分是这样的:想想迪斯科舞会,你就是DJ,你不会把你最喜欢的歌曲命名为&#34;所有流程都已完成&#34; ,直到您在舞池上计算 5个人。每当有人进来时,你都会倒计时(从5开始),当你达到0时 - 你就会把自己喜欢的歌曲放进去。你不在乎他们是否在舞池里,你只关心你在舞池里有5个人。
现在,对于您的示例,您将countDownLatch = 5并且您的for循环已经<7(可以)。所以当5位舞者进来时 - 你把歌曲放到了#34;所有的过程都完成了#34 ;;所以结果还可以。
你问:CountDownLatch&#39;倒计时锁定&#39; (等待完成 线程数)根据给出的线程数 声明?
不,它不会等待线程完成,它根本不关心线程,它只关心你的数量。
在现实世界中,当您想要确保所有线程都已执行特定任务并且现在您已准备好继续使用main(或不同的线程)时,您将使用此机制。
答案 2 :(得分:1)
尽管我的答案为时已晚,但希望这对像我这样不熟悉线程的人有所帮助。根据我的分析,倒计时锁定按预期工作。请进行此更改并查看输出
@Override
public void run() {
//System.out.println("Starting: "+id);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println("Finished the thread: "+id);
}
答案 3 :(得分:0)
CountDownLatch用数字初始化。这个数字表示在等待await()的线程可以获得的线程之前必须调用countDown()的次数。
new CountDownLatch(5)
countDown()必须被调用5次,然后才能获得等待await()的线程。 所以将代码更改为
new CountDownLatch(7)
答案 4 :(得分:0)
它独立于线程。
当有人在倒计时锁存器上调用等待时,如果计数器不为零,它们将阻塞。它们将继续阻塞,直到计数器达到零,或者直到它们达到超时(如果它们调用等待的版本让你超时)。
你可以让一个线程正在工作,并递减计数器和另一个等待这些事情的线程。您还可以使用值为1的倒计时锁存器,并在构建gui时让另一个线程进行初始化。然后在使gui可见之前,在闩锁上有主线程块。有很多可能性。
答案 5 :(得分:0)
CountDownLatch
允许线程在countDown
锁存器中的所有许可证后通过。
在这种情况下,由于你有5个许可,但 7 个线程,第5个线程将解锁锁存器,程序继续。但是,你仍然有两个线程。
由于锁存器已解锁,程序会在没有最后2个线程的情况下继续运行,因为countDown
没有许可证。要解决这个问题,只需要7个许可证而不是5个。