本节介绍了闭锁的用法,举了使用CountDownLatch控制进程的例子。示例代码目标为让所有线程创建后等待,全部创建完成后再一起执行,测试并发执行任务的时间。但根据实际测试发现结果有误。特此记录。
P79 在计时测试中使用CountDownLatch来启动和停止线程示例代码,添加输出和注释
class TestHarness {
public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
System.out.println(Thread.currentThread().getId() + " await");
startGate.await();
try {
System.out.println(Thread.currentThread().getId() + " run");
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored) {
}
}
};
t.start();
}
long start = System.nanoTime();
// 所有线程创建完后,由主线程countdown,startGate状态值为0,之前创建的线程进入就绪
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
执行
public class App {
public static void main(String[] args) throws Exception {
System.out.println("time:" + new TestHarness().timeTasks(5, () -> {}));
}
}
输出结果
16 run
17 await
17 run
19 await
19 run
18 await
18 run
15 await
15 run
time:9583200
显然与预期不符
解析
问题在于主线程startGate.countDown()执行时,前面for循环创建的线程可能还未执行startGate.await(),导致无法同时释放所有线程。
可以使用CyclicBarrier类解决
class FixedTestHarness {
public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
// final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
final CyclicBarrier cyclicBarrier = new CyclicBarrier(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
System.out.println(Thread.currentThread().getId() + " await");
// startGate.await();
cyclicBarrier.await();
try {
System.out.println(Thread.currentThread().getId() + " run");
task.run();
} finally {
endGate.countDown();
}
} catch (Exception ignored) {
}
}
};
t.start();
}
long start = System.nanoTime();
// startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
结果
16 await
15 await
17 await
18 await
19 await
19 run
18 run
17 run
15 run
16 run
time:5743800