Java并发编程中级篇(三):使用CountDownLatch控制多线程并发等待

原创
2016/11/26 11:20
阅读数 7.3K

你是否遇到这这样一种情况,我们要举行一个视频会议,有若干的参会人员,需要等待所有的人员到齐后视频会议才能开始。

为了解决这个问题,Java API提供了一个线程同步辅助类CountDownLatch,使用这个辅助类可以让线程等待其它线程完成一组操作后才能执行,否则就一直等待。这个类使用一个整形参数来初始化,这个整形参数代表着等待其他线程的数量,使用await()方法让线程开始等待其他线程执行完毕,每一个线程执行完毕后后调用countDown()方法,这个方法会让CountDownLatch内部的计数器减1,当计数器变为0的时候,CountDownLatch类将唤醒所有调用await()方法并进入WAITING状态线程。

下面我们来完成这个视频会议的例子:

创建视频会议线程类VideoConference,并声明一个CountDownLatch属性controller来控制视频会议线程等待所有参会者到齐。会议线程启动后会调用await()方法并进入等待状态,每一个参会者到达后代用arrive()方法,并把controller中的计数器减1,当计数器等于0的时候会议线程继续执行。

public class VideoConference implements Runnable{
    private final CountDownLatch controller;

    public VideoConference(int number) {
        controller = new CountDownLatch(number);
    }

    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());
    }

    @Override
    public void run() {
        System.out.printf("VidwoConference: Initialization: %d participants.\n", controller.getCount());

        try {
            controller.await();
            System.out.printf("VidwoConference: All the participants have come.\n");
            System.out.printf("VidwoConference: Let's start...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

创建参会者线程类Participant,这个类持有会议类的引用,启动后用一个随机休眠时间来模拟与会者到达所需的时间,休眠结束后代用会议类的arrive(name)方法告诉会议类,与会者到达并把CountDownLatch计数器减1。

public class Participant implements Runnable{
    private String name;
    private VideoConference videoConference;

    public Participant(String name, VideoConference videoConference) {
        this.name = name;
        this.videoConference = videoConference;
    }

    @Override
    public void run() {
        long duration = (long) (Math.random() * 10);
        try {
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        videoConference.arrive(name);
    }
}

实现主方法类,这里我们创建并移动一个视频会议,规定参会者有10个人。然后我们启动10个参会者线程,当所有参会者都到达后,视频会议开始执行。

public class Main {
    public static void main(String[] args) {
        VideoConference videoConference = new VideoConference(10);
        Thread threadConference = new Thread(videoConference);
        threadConference.start();

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(new Participant("P-" + i, videoConference));
        }

        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

查看控制台日志,我们看到每次有一个参会者到达都可以调用getCount()方法来获取计数器的值。

VidwoConference: Initialization: 10 participants.
P-0 has arrived.
VideoConference: Waiting for 9 participants.
P-4 has arrived.
VideoConference: Waiting for 8 participants.
P-2 has arrived.
VideoConference: Waiting for 7 participants.
P-5 has arrived.
VideoConference: Waiting for 6 participants.
P-8 has arrived.
VideoConference: Waiting for 5 participants.
P-3 has arrived.
VideoConference: Waiting for 4 participants.
P-6 has arrived.
VideoConference: Waiting for 3 participants.
P-1 has arrived.
VideoConference: Waiting for 2 participants.
P-7 has arrived.
VideoConference: Waiting for 1 participants.
P-9 has arrived.
VideoConference: Waiting for 0 participants.
VidwoConference: All the participants have come.
VidwoConference: Let's start...

注意,CountDownLatch并不是用来保护共享资源同步访问的,而是用来控制并发线程等待的。并且CountDownLatch只允许进入一次,一旦内部计数器等于0,再调用这个方法将不起作用,如果还有第二次并发等待,你还得创建一个新的CountDownLatch。

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部