java并发编程实战手册第三章---CountDownLatch使用

一、等待多个并发事件的完成
本案例使用CountDownLatch实现,也叫倒计数锁存器(话说,这样不知道对不对),
本范例实现视频会议系统,这个视频会议系统将等待所有的参与者都到齐才开始。
看源码:
1.视频会议类Videoconference

/**
 * 
 * @author fcs
 * @date 2015-4-19
 * 描述: 等待多个并发事件的完成
 * 说明:可以使用countDownLacth实现,该类是同步辅助类
 * 
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行
CountDownLatch如其所写,是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。下面以一个模拟运动员比赛的例子加以说明。
*/

//创建视频会议类
public class Videoconference implements Runnable{
    private final CountDownLatch controller;

    //初始化倒数计数器
    public Videoconference(int number){
        controller = new CountDownLatch(number);
    }
    @Override
    public void run() {
        System.out.printf("VideoConference: Initialization: %d participants.\n",controller.getCount());
        try {
            controller.await();   //使用该方法等待所有与会者到达,相当于所有线程都在同一个地点到达,该方法会抛出异常,进行捕获处理
            System.out.printf("VideoConference: All Participants have come\n");
            System.out.printf("VideoConference: Let's start ... \n");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 
     * 作者:fcs
     * 描述:与会者进入视频会议的时候,这个方法将被调用
     * 说明:
     * 返回:
     * 参数:
     * 时间:2015-4-19
     */
    public void arrive(String name){
        System.out.printf("%s has arrived .\n",name);
        controller.countDown();   //内部计数器将减一
        System.out.printf("VideoConference: Waiting for %d participants.\n",controller.getCount());
    }
}

2.创建与会者类

/**
 * 
 * @author fcs
 * @date 2015-4-19
 * 描述:创建与会者类,实现Runnable接口
 * 说明:
 */
public class Participant implements Runnable{

    private Videoconference  videoconference;
    private String name;
    public Participant(Videoconference videoconference,String name){
        this.videoconference = videoconference;
        this.name = name;
    }

    @Override
    public void run() {
        long duration = (long)(Math.random() * 10);
        try {
            TimeUnit.SECONDS.sleep(duration);
            videoconference.arrive(name);   //调用视频会议对象的arrive()方法,表明一个与会者的到来
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.测试类

public class Main {
    public static void main(String[] args) {
        //创建视频会议对象,等待10个与会者到来
        Videoconference  videoconference = new Videoconference(10);
        Thread thread = new Thread(videoconference);
        thread.start();
        for(int i =0;i< 10;i++){
            Participant participant = new Participant(videoconference, "Participant"+i);
            Thread th = new Thread(participant);
            th.start();
        }
    }
}

4.测试演示
java并发编程实战手册第三章---CountDownLatch使用_第1张图片

分析:
CountDownLatch: java语言提供的同步辅助类,在完成一组正在其他线程中执行的操作前,允许线程一直等待。
内部实现也是有一个内部计数器表明有多少要完成的操作,当一个操作完成后计数器减一。
所有线程完成工作后都将在同一个集合点集合。
该类有三个基本元素
1.一个初始值,即定义必须等待的现行完成的操作的数目。
2.await()方法,需要等待其他事件先完成的线程调用;比如去旅游,几个人等待其他几个人,等待者将调用该方法等待没有来的人。
3.countDownLatch()方法,每个被等待的时间在完成的时候调用,该方法将内部计数器减一;当被等待者到达时被等待的人就会少一个。

 说明:  
     1.当3中的方法将计数器减到0 的时候,CountDownLatch对象将唤醒所有在await()方法上等待的线程;这个方法可以被看做是去旅游的一个站点,都在该站点集合才能上车。
     2.当CountDownLatch对象的内部计数器初始化后,就不能被再次初始化或者修改。唯一能改变参数值的就是3中的方法,当计数器变成0,await()方法已经调用,再调用
     countDown()方法将不会起作用。
 CountDownLatch机制与其他同步方法的不同点
     1.该机制不是用来保护共享资源或者临界区的,是用来同步多个任务或者多个线程的。
     2.该对象只允许进入一次,一旦该内部计数器变成0的时候再调用countDown()方法将不起作用。如果要做类似的同步就必须创建新的CountDownLatch对象。
   该类有另外一个await方法await(long time, TimeUnit unit),该方法被调用后,线程将休眠直到被中断,或者CountDownLatch的内部计数器达到0,或者指定的时间已经过期。
   3.等待多个并发事件的完成(这里的任务是一个阶段可以完成的,如果需要两个或多个阶段完成该任务,那么CountDownLatch就不是一个了)
      这里的演示范例是指多个线程完成任务后将在一个集合点集合。
      比如视屏会议中等待所有与会者的到达才开始会议,或者所有的同学都在一个汽车站中集合才能一起出发春游。
    该实例使用CountDownLatch实现,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 

这个类使用一个整数进行初始化,这个整数就是线程要等待完成的操作的数目。当一个线程要等待某些操作先执行完时,需要调用await()方法,
该方法让线程进入休眠直到等待的所有操作都完成。当一个操作完成后,它将调用countDown()方法将CountDownLatch类的内部计数器减1。
当计数器变成0的时候,CountDownLatch类将唤醒所有调用await()方法而进入休眠的线程。

上面的是一些书上的见解和我的浅显的见解,在JDK文档中的描述如下:
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

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

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

另一种典型用法是,将一个问题分成 N 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。当所有的子部分完成后,协调线程就能够通过 await。(当线程必须用这种方法反复倒计数时,可改为使用 CyclicBarrier。)

内存一致性效果:线程中调用 countDown() 之前的操作 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。

你可能感兴趣的:(java多线程专题)