Java并发新构件之CounDownLatch

    CountDownLatch主要用于同步一个或多个任务,强制它们等待由其余任务执行的一组操做完成。html

    你能够向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()的方法都将阻塞,直到这个计数值达到0。其余任务在结束其工做时,能够在该对象上调用countDown()来减少这个计数值,你能够经过调用getCount()方法来获取当前的计数值。CountDownLatch被设计为只触发一次,计数值不能被重置。若是你须要可以重置计数值的版本,则可使用CyclicBarrier。java

    调用countDown()的任务在产生这个调用时并无阻塞,只有对await()的调用会被阻塞,直到计数值到达0。并发

    CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决任务,并建立值为0的CountDownLatch。当每一个任务完成时,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们本身锁住,直到锁存器计数结束。下面是演示这种技术的一个框架示例:框架

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class TaskPortion implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private static Random random = new Random();
    private final CountDownLatch latch;
    public TaskPortion(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            doWork();
            latch.countDown();//普通任务执行完后,调用countDown()方法,减小count的值
            System.out.println(this + " completed. count=" + latch.getCount());
        } catch (InterruptedException e) {
            
        }
    }
    
    public void doWork() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));
    }
    
    @Override
    public String toString() {
        return String.format("%1$-2d ", id);
    }
}

class WaitingTask implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final CountDownLatch latch;
    public WaitingTask(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            //这些后续任务须要等到以前的任务都执行完成后才能执行,即count=0时
            latch.await();
            System.out.println("Latch barrier passed for " + this);
        } catch (InterruptedException e) {
            System.out.println(this + " interrupted.");
        }
    }
    
    @Override
    public String toString() {
        return String.format("WaitingTask %1$-2d ", id);
    }
}

public class CountDownLatchDemo {
    static final int SIZE = 10;
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(SIZE);
        ExecutorService exec = Executors.newCachedThreadPool();
        //5个WaitingTask
        for (int i = 0; i < 5; i++) {
            exec.execute(new WaitingTask(latch));
        }
        //10个任务,这10个任务要先执行才会执行WaitingTask
        for (int i = 0; i < SIZE; i++) {
            exec.execute(new TaskPortion(latch));
        }
        System.out.println("Launched all tasks.");
        exec.shutdown();//当全部的任务都结束时,关闭exec
    }
}

执行结果(可能的结果):dom

Launched all tasks.
4   completed. count=9
6   completed. count=8
3   completed. count=7
0   completed. count=6
2   completed. count=5
1   completed. count=4
5   completed. count=3
7   completed. count=2
9   completed. count=1
8   completed. count=0
Latch barrier passed for WaitingTask 0  
Latch barrier passed for WaitingTask 2  
Latch barrier passed for WaitingTask 1  
Latch barrier passed for WaitingTask 3  
Latch barrier passed for WaitingTask 4

    从结果中能够看到,全部的WaitingTask都是在全部的TaskPortion执行完成以后执行的。ide

    TaskPortion将随机的休眠一段时间,以模拟这部分工做的完成。而WaitingTask表示系统中必须等待的部分,它要等到问题的初始部分完成后才能执行。注意:全部任务都使用了在main()中定义的同一个CountDownLatch对象this

一个更实际的例子(参考自这里,有改动):N个选手比赛跑步,在枪响后同时起跑,所有到达终点后比赛结束,下面是代码:线程

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

/**
 * 用CountDownLatch来模拟运动员赛跑的Demmo.
 */
public class CountDownLatchDemo {
    /** 参与比赛的人数(并发线程数) */
    private static int PLAYER_NUM = 8;
    
    public static void main(String[] args) throws Exception {
        //用于让主线程(裁判)等待全部子线程(运动员)就绪
        final CountDownLatch readySignal = new CountDownLatch(PLAYER_NUM);
        //用于让子线程(运动员)等待主线程(裁判)发号施令
        final CountDownLatch beginSignal = new CountDownLatch(1);
        //用于让主线程(裁判)等待全部子线程(运动员)执行(比赛)完成
        final CountDownLatch endSignal = new CountDownLatch(PLAYER_NUM);
        ExecutorService executorService = Executors.newFixedThreadPool(PLAYER_NUM);
        
        for(int i = 0; i < PLAYER_NUM; i++){
            final int num = i + 1;
            Runnable player = new Runnable(){
                @Override
                public void run() {
                    System.out.println("No." + num + " is ready");
                    //一个任务就绪后,减小readySignal上的等待
                    readySignal.countDown();
                    try {
                        //等待主线程比赛开始的命令
                        beginSignal.await();
                        System.out.println("No." + num + " is running");
                        Thread.sleep((long) (Math.random() * 5000));
                        System.out.println("No.-----" + num + " arrived");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //每一个线程执行完成后,调用countDown减小在endSignal上的等待线程数
                        endSignal.countDown();
                    }
                }
            };
            executorService.execute(player);
        }
        
        //这里让主线程等待,确保全部子线程已开始执行并调用了await进入等待状态
        readySignal.await();
        System.out.println("Game Start!");
        //全部等待在beginSignal上的线程(运动员)开始执行(比赛)
        beginSignal.countDown();
        try {
            //等待全部在endSignal上的线程(运动员)执行(比赛)完成
            endSignal.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Game Over!");
            executorService.shutdown();
        }
    }
}

代码中用到了3个CountDownLatch对象,具体做用已在代码中说明,这里不赘述。设计

说一下执行过程:code

  1. 主线程(main方法)启动后,向ExecutorService中提交了8个任务(子线程),每一个任务开始执行后都在等待主线程说“比赛开始”。
  2. 主线程等待线程池中的任务所有都处于“ready”状态,而后鸣枪,比赛开始。
  3. 主线程等待全部任务执行完成。
  4. 子线程各自执行,而后陆续到达终点,完成比赛。
  5. 主线程宣告“比赛结束”,输出“Game Over!”。

下面是执行结果:

No.1 is ready
No.2 is ready
No.3 is ready
No.4 is ready
No.6 is ready
No.5 is ready
No.7 is ready
No.8 is ready
Game Start!
No.1 is running
No.4 is running
No.3 is running
No.6 is running
No.2 is running
No.5 is running
No.7 is running
No.8 is running
No.-----3 arrived
No.-----2 arrived
No.-----1 arrived
No.-----8 arrived
No.-----4 arrived
No.-----6 arrived
No.-----7 arrived
No.-----5 arrived
Game Over!
相关文章
相关标签/搜索