Java并发编程札记-(四)JUC锁-08CountDownLatch

CountDownLatch是一个通用同步器,用于同步一个或多个任务。在完成一组正在其他线程中执行的任务之前,它允许一个或多个线程一直等待。

可以用一个初始计数值来初始化CountDownLatch对象,任何在这个对象上调用wait()的方法都将阻塞,直至计数值到达0。每完成一个任务,都可以在这个对象上调用countDown()减少计数值。当计数值减为0,所有等待的线程都会被释放。CountDownLatch的计数值不能重置。如果需要重置计数器,请考虑使用CyclicBarrier。

使用场景

作为一个通用同步工具,CountDownLatch有许多用途。比如,将计数值初始化为1的CountDownLatch用作一个简单的开/关:在通过调用countDown()的线程打开入口前,所有调用await()的线程都一直在入口处等待;用N初始化的 CountDownLatch可以使一个线程在N个线程完成某项操作之前一直等待,或者使其在某项操作完成N次之前一直等待。

例1:CountDownLatch作为开关

public class Driver {
    public static void main(String args[]) throws InterruptedException {
        int n = 5;
        // 启动信号,在driver为继续执行worker做好准备之前,它会阻止所有的worker继续执行。
        CountDownLatch startSignal = new CountDownLatch(1);
        // 完成信号,它允许driver在完成所有 worker之前一直等待。
        CountDownLatch doneSignal = new CountDownLatch(n);

        for (int i = 0; i < n; ++i) // 创建并启动所有线程
            new Thread(new Worker(startSignal, doneSignal)).start();

        Thread.sleep(1000);

        startSignal.countDown(); // 打开startSignal开关,执行所有等待的任务

        doneSignal.await();// 等待doneSignal计数器为0,即所有任务执行完

        System.out.println("down");
    }
}

class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;

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

    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "启动了");
            startSignal.await();// 使当前线程在锁存器startSignal倒计数至零之前一直等待
            System.out.println(Thread.currentThread().getName() + "工作了");
            doneSignal.countDown();// 递减锁存器doneSignal的计数
        } catch (InterruptedException ex) {
        } 
    }
}

执行结果为:
Thread-2启动了
Thread-0启动了
Thread-1启动了
Thread-3启动了
Thread-4启动了
Thread-2工作了
Thread-3工作了
Thread-1工作了
Thread-4工作了
Thread-0工作了
down

从结果中看到,5个任务线程依次启动,但每一个都没有执行完,这是因为run方法中的startSignal.await();使当前线程在锁存器startSignal倒计数至零之前一直等待。直到startSignal.countDown(); 打开startSignal开关,才执行所有等待的任务。

例2:CountDownLatch分割任务

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Driver2 {
    public static void main(String args[]) throws InterruptedException {
        int n = 5;
        ExecutorService exec = Executors.newCachedThreadPool();
        // 完成信号,它允许driver在完成所有 worker之前一直等待。
        CountDownLatch doneSignal = new CountDownLatch(n);
        // 创建并启动所有线程
        for (int i = 0; i < n; ++i)
            exec.execute(new WorkerRunnable(doneSignal, i));
        // 等待doneSignal计数器为0,即所有任务执行完
        doneSignal.await();
        System.out.println("down");
        exec.shutdown();
    }
}

class WorkerRunnable implements Runnable {
    private final CountDownLatch doneSignal;
    private final int i;

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

    public void run() {
        System.out.println("任务" + i + "完成了");
        doneSignal.countDown();// 递减锁存器doneSignal的计数
    }
}

源码

待补充

本文就讲到这里,想了解Java并发编程更多内容请参考:

  • Java并发编程札记-目录
    原文作者:java并发
    原文地址: http://blog.csdn.net/panweiwei1994/article/details/78826072
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞