Java并发实践中的CountDownLatch示例

3
我正在学习《Java并发编程实践》这本书,并遇到了CountDownLatch。
以下是给出的示例:
public 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 {
                        startGate.await();
                        try {
                            task.run();
                        } finally {
                            endGate.countDown();
                        }
                    } catch (InterruptedException ignored) { }
                }
            };
            t.start();
        }
        long start = System.nanoTime();
        startGate.countDown();
        endGate.await();
        long end = System.nanoTime();
        return end-start;
    }
}

这是它的解释:
TestHarness在5.11清单中展示了锁存器(latch)的两种常见用途。TestHarness创建了多个线程并发运行给定的任务。它使用了两个锁存器,一个“起始闸门”和一个“结束闸门”。起始闸门的计数初始化为1;结束闸门的计数初始化为工作线程数。每个工作线程做的第一件事情是等待起始闸门;这确保了在所有线程都准备好开始之前,它们都不会开始工作。它们做的最后一件事情是在结束闸门上倒数;这样主线程就可以高效地等待,直到所有工作线程都完成,以便它可以计算流逝的时间。
我对Java多线程还不熟悉,所以我无法理解给出的解释以及这个程序是如何工作的。
这句话的意思是什么:
每个工作线程做的第一件事情是等待起始闸门;这确保了在所有线程都准备好开始之前,它们都不会开始工作。它们做的最后一件事情是在结束闸门上倒数;这样主线程就可以高效地等待,直到所有工作线程都完成,以便它可以计算流逝的时间。
这段代码的工作原理是什么:
``` public static 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 { startGate.await(); try { task.run(); } finally { endGate.countDown(); } } catch (InterruptedException ignored) { } } }; t.start(); } long start = System.nanoTime(); startGate.countDown(); endGate.await(); long end = System.nanoTime(); return end - start; } ```
Thread t = new Thread() {

    public void run() {
        try {
            startGate.await();
            try {
                task.run();
            } finally {
                endGate.countDown();
            }
        } catch (InterruptedException ignored) { }
    }
};

and

long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end-start;

请帮助我理解这个概念。

3
如果你不知道多线程是如何工作的,为什么要从 CountDownLatch 开始呢?要慢下来,从这里开始:http://docs.oracle.com/javase/tutorial/essential/concurrency/。请注意,我已经翻译了整段话并且没有添加任何额外的信息。 - Sotirios Delimanolis
@SotiriosDelimanolis,我正在学习多线程概念,使用这本书作为参考,因为在SO上有很多专家推荐。但是我在理解这个例子时遇到了困难。 - Chaitanya
2
这本书不应该被用作多线程的入门,因为您可以在如何使用本书一节中清楚地阅读到。同一节还列出了建议的入门来源。 - PM 77-1
启动线程需要时间。因此,如果每个线程一旦启动就立即执行其任务,那么有些线程将已经在运行,而另一些线程尚未被创建。因此,它们使用闩锁等待,直到主线程发出启动信号。就像真正的赛跑一样。当它们完成任务时,它们会发出信号,以便主线程知道它们何时全部完成。 - JB Nizet
2
当比赛开始之前,所有的马都站在起跑线上,比赛才会开始;当最后一匹马冲过终点线时,比赛才会结束。就像是参加一场马赛一样。 - PM 77-1
4个回答

5
这段代码
Thread t = new Thread() {

    public void run() {
        try {
            startGate.await();
            try {
                task.run();
            } finally {
                endGate.countDown();
            }
        } catch (InterruptedException ignored) { }
    }
};

此段代码设置了所有所需的线程。每个线程将等待startGate被“打开”,即其计数为0。当线程执行完Runnable, 即run()返回时,它们将倒数endGate。这就是这段代码的含义。

每个工作线程的第一件事是等待启动门;这确保了在所有线程都准备好开始工作之前,它们都不会开始工作。每个工作线程最后要做的事情是在结束门上倒数;

这意味着当所有线程都设置好后,将执行此代码。

long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end-start;

当前线程倒计时startGate,允许所有其他线程开始执行它们的Runnable,即task.run()。然后在endGate上等待(阻塞),直到计数降至0。此时,它计算所需时间并返回该值。这就是此语句的含义:

这使得主线程可以有效地等待直到所有工作线程完成,以便它可以计算经过的时间。


2

实际上,您的问题并不是非常清楚,或者只要您已经了解的内容不清楚,就不清楚应该如何解释代码。

然而,这里使用的概念并不是很复杂,通过去除错误处理并使用内联注释指出重要部分,可能已经变得更加清晰:

for (int i = 0; i < nThreads; i++) {

    // This will create and start a new thread
    // that executes the code in the "run" method
    Thread t = new Thread() {
        public void run() {

            // Wait until this thread gets the start signal
            startGate.await();

            // Perform some arbitrary work
            task.run();

            // Notify everybody that this thread has finished his work
            endGate.countDown();
        }
    };
    t.start(); 
}

// Give all threads the start signal: Counting down
// where will cause the latch to reach the value 0,
// and every thread that is waiting at "startGate.await()"
// will proceed his work
startGate.countDown();

// Wait here until all threads have called "endGate.countDown()".
endGate.await();

1

startGate最初设置为1,因此所有线程都将等待直到它被倒计数。

endGate初始化为我们计划使用的线程数,因此它将等待直到所有线程通过结束门之前报告所花费的时间。

// Take a note of the time.
long start = System.nanoTime();
// Start all of the threads running - they all wait for this signal by calling startGate.await().
startGate.countDown();
// Wait for all threads to complete - they record their finish with endGate.countDown().
endGate.await();
// Note the time.
long end = System.nanoTime();

-1
package com.sample.thread;

import java.util.concurrent.CountDownLatch;

public class CDLExample {

private static CountDownLatch latch;

public static void main(String args[]) {
    latch = new CountDownLatch(2);

    new Thread("main") {
        public void run() {
            try {
                latch.await();
                System.out
                        .println("\nStarting Main thread as all other thread is started");
                System.out.println("CountDownLatch demonstrated: "
                        + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }.start();

    new Thread("first") {
        public void run() {
            System.out.println("Print Odd Number: "
                    + Thread.currentThread().getName());
            try {
                for (int i = 1; i < 20; i = i + 2) {
                    System.out.print(i + " ");
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
        }
    }.start();

    new Thread("second") {
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println("\nPrint Even Number: "
                        + Thread.currentThread().getName());

                for (int i = 2; i < 20; i = i + 2) {
                    Thread.sleep(50);
                    System.out.print(i + " ");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            latch.countDown();
        }
    }.start();
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接