《Java并发编程实战》笔记——5.5.1CountDownLatch闭锁代码修正

221 阅读1分钟

本节介绍了闭锁的用法,举了使用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