同步器之CountDownLatch

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier

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

CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await

 

CountDownLatch主要起倒计时计数器作用,它主要有两个方法await()和countDown()。一旦某个线程调用await()方法,那么该线程就会阻塞,等待CountDownLatch计数器倒计时归零,需要注意的是尽管线程调用await()方法后会阻塞,但是CountDownLatch允许别的线程调用countDown()方法,将计数器减一。也就是说调用计时器的线程阻塞后,可以利用别的线程控制调用线程何时从新开始运行。

 

 

import java.util.concurrent.CountDownLatch;

/**
 * 该demo主要想要做的事就是:在主线程中创建N个支线程,让支线程等待主线程将开关计数器startSignal打开。
 * 而当主线程打开startSignal开关后,主线程要等待计数器doneSignal归零,
 * 而doneSignal计数器归零依赖于每个支线程为主线程的计数器减一。
 * 所以当主线程打开开关后,支线程才能运行完毕,而只有支线程全部运行完毕,才能打开主线程的计数器。 这样整个程序才能走完
 * 
 */
public class LatchDriverDemo {
    public static final int N = 5;

    public static void main(String[] args) throws InterruptedException {
        // 用于向工作线程发送启动信号
        CountDownLatch startSignal = new CountDownLatch(1);
        // 用于等待工作线程的结束信号
        CountDownLatch doneSignal = new CountDownLatch(N);
        // 创建启动线程
        System.out.println("开始创建并运行分支线程,且分支线程启动startSignal计数器,等待主线程将startSignal计数器打开");
        for (int i = 0; i < N; i++) {
            new Thread(new LatchWorker(startSignal, doneSignal), "t" + i).start();
        }
        // 主线程,递减开始计数器,让所有线程开始工作
        System.out.println("主线程" + Thread.currentThread().getName() + "将startSignal计数器打开");
        startSignal.countDown();
        // 主线程阻塞,等待所有线程完成
        System.out.println("主线程" + Thread.currentThread().getName() + "开始倒计时5个数");
        doneSignal.await();
        /*
         * 为什么说运行到下一句,所有线程就全部运行完毕了呢。 因为主线程要倒计时5个数, 而产生的5个支线程在运行完毕前会将主线程的计数器减一,
         * 所以如果所有支线程运行完毕了 ,主线程才能继续运行主线程的最后一个打印程序
         */
        System.out.println("所有线程运行完毕");
    }
}

class LatchWorker implements Runnable {
    // 用于等待启动信号
    private final CountDownLatch startSignal;
    // 用于发送结束信号
    private final CountDownLatch doneSignal;

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

    public void run() {
        try {
            // 一旦调用await()方法,该线程就会开始阻塞。直到计数器startSignal为0
            System.out.println(Thread.currentThread().getName() + " 开始调用await()方法,等待计数器startSignal被主线程打开");
            startSignal.await();
            doWork();
            System.out.println(Thread.currentThread().getName() + " 将主线程的计数器减一");
            doneSignal.countDown();// 发送完成信号
        } catch (InterruptedException ex) {
        }
    }

    void doWork() {
        System.out.println(Thread.currentThread().getName() + " 的计数器被打开,分支线程开始运行");
        int sum = 0;
        for (int i = 0; i < 10000; i++) {
            sum += i;
        }
    }
}

 

运行结果:

开始创建并运行分支线程,且分支线程启动startSignal计数器,等待主线程将startSignal计数器打开
t0 开始调用await()方法,等待计数器startSignal被主线程打开
t1 开始调用await()方法,等待计数器startSignal被主线程打开
t2 开始调用await()方法,等待计数器startSignal被主线程打开
t3 开始调用await()方法,等待计数器startSignal被主线程打开
主线程main将startSignal计数器打开
t4 开始调用await()方法,等待计数器startSignal被主线程打开
t0 的计数器被打开,分支线程开始运行
t3 的计数器被打开,分支线程开始运行
主线程main开始倒计时5个数
t2 的计数器被打开,分支线程开始运行
t1 的计数器被打开,分支线程开始运行
t4 的计数器被打开,分支线程开始运行
t3 将主线程的计数器减一
t1 将主线程的计数器减一
t4 将主线程的计数器减一
t0 将主线程的计数器减一
t2 将主线程的计数器减一
所有线程运行完毕

 

 

posted @ 2012-06-14 16:01  一筐  阅读(473)  评论(0编辑  收藏  举报