JDK1.8并发包之 -- CountDownLatch

上篇 JDK1.8并发包之--Semaphore 发现一个方法,搞懂Jdk的类注释,就能快速了解该类的用法,于是从CountDownLatch的英文注释出发,GO!

1、第一段,实现目标
A synchronization aid that allows one or more threads to wait until
 a set of operations being performed in other threads completes.

一句话告诉我们CountDownLatch的目标是,等待一组线程执行完成。场景如下,10个线程并发执行一系列任务,肯定有的线程先跑完,那么先跑完的就等省下没跑完的线程执行完毕,再执行下面任务。

2、第二段,运行机制
A {@code CountDownLatch} is initialized with a given count.
The {@link #await await} methods block until the current count reaches
 zero due to invocations of the {@link #countDown} method, after which
 all waiting threads are released and any subsequent invocations of
 {@link #await await} return immediately.  This is a one-shot phenomenon
 -- the count cannot be reset.  If you need a version that resets the
 count, consider using a {@link CyclicBarrier}.

CountDownLatch初始化构造传递整形count参数,await方法直到count降为0才会立即返回,调用countDown方法使count减一,count等于0后,所有的等待线程被唤醒。这个过程中count不能被重置,如果要重置count,考虑使用CycliBarrier。

3、第三段,用途
A {@code CountDownLatch} is a versatile synchronization tool
 and can be used for a number of purposes.  A
 {@code CountDownLatch} initialized with a count of one serves as a
 simple on/off latch, or gate: all threads invoking {@link #await await} 
 wait at the gate until it is opened by a thread invoking {@link
 #countDown}.  A {@code CountDownLatch} initialized to N
 can be used to make one thread wait until N threads have
 completed some action, or some action has been completed N times.

CountDownLatch有种用途,可以用来作为门栓或者门岗,所有的线程调用await方法阻塞,直到一个线程调用countDown减1,CountDownLatch初始化为N,保证一个线程等待N个线程完成动作后,或者执行N次后,再接着执行。

4、第四段
A useful property of a {@code CountDownLatch} is that it
 doesn't require that threads calling {@code countDown} wait for
 the count to reach zero before proceeding, it simply prevents any
 thread from proceeding past an {@link #await await} until all
 threads could pass.

CountDownLatch有个非常有用的特性就是调用countDown减1不用等待减到0 。仅仅是避免其他线程都执行完了才能通过await方法。

5、第五段 案例
Here is a pair of classes in which a group
 * of worker threads use two countdown latches:
 * 
    *
  • The first is a start signal that prevents any worker from proceeding * until the driver is ready for them to proceed; *
  • The second is a completion signal that allows the driver to wait * until all workers have completed.

说的两处使用CountDownLatch的场景,第一个CountDownLatch避免worker执行任务,直到driver准备好了,worker才开始执行。第二个CountDownLatch是信号量,让driver等待worker执行完毕。

@Slf4j
public class Driver {

    private static final int N = 10;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(N);
        for (int i = 0; i < N; ++i) {
            new Thread(new Worker(startSignal, doneSignal, "Thread-" + i)).start();
        }

        doSomethingElse();
        startSignal.countDown();
        doSomethingElse();
        doneSignal.await();
    }

    static void doSomethingElse() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        log.info("线程:{} 睡眠2秒", Thread.currentThread().getName());
    }

    static class Worker extends Thread {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;

        public Worker(CountDownLatch startSignal, CountDownLatch doneSignal, String name) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.setName(name);
        }

        @Override
        public void run() {
            try {
                startSignal.await();
                doWork();
                doneSignal.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        void doWork() throws InterruptedException {
            TimeUnit.SECONDS.sleep(3);
            log.info("线程:{} 睡眠3秒", this.getName());
        }
    }
}

还有另外一种用法是,把一个问题拆分成N个部分,每个部分都是一个线程任务,然后执行countDwon直到为0,后续的请求放入队列中缓存起来。当N个部分全部执行完成,协调线程就会通过await,进一步执行任务。另外,如果线程需要重复执行countDown,就要用CyclicBarrier。

Another typical usage would be to divide a problem into N parts,
 * describe each part with a Runnable that executes that portion and
 * counts down on the latch, and queue all the Runnables to an
 * Executor.  When all sub-parts are complete, the coordinating thread
 * will be able to pass through await. (When threads must repeatedly
 * count down in this way, instead use a {@link CyclicBarrier}.)
@Slf4j
public class CountDownLatchDemo2 {
    private static final int N = 10;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch doneSignal = new CountDownLatch(N);
        Executor e = Executors.newCachedThreadPool();

        for (int i = 0; i < N; ++i) {
            e.execute(new Worker(doneSignal, i));
        }
        System.out.println(doneSignal.getCount());

        doneSignal.await();
        System.out.println(doneSignal.getCount());
        doneSignal = new CountDownLatch(N);
        for (int i = N; i < N + N; ++i) {
            e.execute(new Worker(doneSignal, i));
        }

        System.out.println(doneSignal.getCount());
    }


    static class Worker extends Thread {
        private final CountDownLatch doneSignal;
        private final int i;

        Worker(CountDownLatch doneSignal, int i) {
            this.doneSignal = doneSignal;
            this.i = i;
        }

        @Override
        public void run() {
            try {
                doWork(i);
                doneSignal.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        void doWork(int i) throws InterruptedException {
            TimeUnit.SECONDS.sleep(i);
            log.info("线程:{} 等待{}秒", this.getName(), i);
        }
    }
}

总结,CountDownLatch有两种用法,第一、协调任务执行关卡,如第一个例子那样,Driver-Worker的关系,好比司机-搬运工,搬运工必须等司机把车开过来才能工作,司机把车开到后,必须等搬运工把所有东西都装完才能把车开到目的地。第二、分别大任务,如第二个例子,把问题分解成N个小份,再分配给N个线程执行,等N个线程执行完毕后再分配下一批任务。笔者用第二种方式同步数据,效率尤其快。

你可能感兴趣的:(JDK1.8并发包之 -- CountDownLatch)