等待多个并发完成

经常会有这种场景:在主线程中启动多个工作线程,然后主线程需要等待工作线程全部完成后再进行下一步处理。如何实现等待多个线程完成?用Thread.join方法?这种方式耦合性太强,而且太土了!Java5引入了新的机制,这个机制语义明确、功能强大、使用灵活,这就是CountDownLatch类。

CountDownLatch类是同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许线程一致等待。这个类的构造函数需要传入一个整数,这个整数就是要等待完成操作的数目。一个线程要等待某些操作现执行完时,需要调用await()方法,这个方法让线程进入休眠直到等待的所有操作完成。当某一个操作完成后,它将调用countDown()方法将CountDownLatch的内部计数器减1。当计数器变成0的时候,CountDownLatch将唤醒所有调用await()方法而进入休眠的线程。

CountDownLatch和其他同步方法不同,它不是用来保护共享资源或者临界区的,而是用来同步执行多个任务的线程。CountDownLatch只准许进入一次,一旦内部计数器为0后,再调用方法将不起作用。

下面一个参加会议的例子说明CountDownLatch的作用和用法。主线程发起多个参会者,每个参会者都是一个线程,主线程需要等到所有参会者都到达之后才能开始会议。示例代码如下:


public class CountDownLatchDemo {

    public static void main(String[] args){
        CountDownLatch latch = new CountDownLatch(10);
        Thread mettingThread = new Thread(new Metting(latch));
        Thread[] assigneerThreads = new Thread[10];
        Assigneer assigneer = new Assigneer(latch) ;

        for(int i=0; i<latch.getCount(); i++){
            Thread t = new Thread(assigneer);
            assigneerThreads[i] = t;
        }

        System.out.println("main:启动会议线程");
        mettingThread.start();
        System.out.println("main:启动参会线程");
        for(int i=0; i<assigneerThreads.length; i++){
            assigneerThreads[i].start();
        }

        //等待参会线程结束
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main:退出");
    }
}

/** 参会线程**/
class Assigneer implements Runnable{
    private CountDownLatch latch ;

    Assigneer(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("参会线程:参会者:" + Thread.currentThread().getName() + "。参加会议....");
        long delay = (long)(Math.random()*1000);
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.latch.countDown();
    }
}

/** 会议线程**/
class Metting implements Runnable{
    private CountDownLatch latch ;

    Metting(CountDownLatch latch){
        this.latch = latch;
    }
    @Override
    public void run() {
        System.out.println("会议线程:参会者数:" + this.latch.getCount() + "。等待参会者....");
        try {
            latch.await();
            System.out.println("会议线程:参会者全部到达,开始会议...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


程序运行日志:

main:启动会议线程
main:启动参会线程
会议线程:参会者数:10。等待参会者....
参会线程:参会者:Thread-1。参加会议....
参会线程:参会者:Thread-3。参加会议....
参会线程:参会者:Thread-2。参加会议....
参会线程:参会者:Thread-4。参加会议....
参会线程:参会者:Thread-5。参加会议....
参会线程:参会者:Thread-6。参加会议....
参会线程:参会者:Thread-8。参加会议....
参会线程:参会者:Thread-7。参加会议....
参会线程:参会者:Thread-9。参加会议....
参会线程:参会者:Thread-10。参加会议....
会议线程:参会者全部到达,开始会议...
main:退出