文档章节

Java并发新构件之CounDownLatch

摆渡者
 摆渡者
发布于 2015/10/13 13:25
字数 1450
阅读 281
收藏 2

精选30+云产品,助力企业轻松上云!>>>

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

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

    调用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
    }
}

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

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执行完成之后执行的。

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

一个更实际的例子(参考自这里,有改动):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对象,具体作用已在代码中说明,这里不赘述。

说一下执行过程:

  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!

 

摆渡者
粉丝 351
博文 175
码字总数 209530
作品 0
成都
程序员
私信 提问
加载中
请先登录后再评论。
(三)线程同步工具集_3---等待多个并发事件

等待多个并发事件 Java concurrent API 提供了一个类,可以使一个或多个线程去等待一系列操作完成,该类是CountDownLatch,该类初始化一个整数,这个整数代表了线程要等待的操作个数, 当一个...

marvin_vov
2014/11/12
8
0
java多线程学习-java.util.concurrent详解(一) Latch/Barrier

Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。从这篇blog起,我将跟大家一起共同学习...

吕兵阳
2015/04/09
42
0
使用 CountDownLatch 控制多个线程执行顺序

已同步更新至:http://dxjia.cn/2015/08/countdownlatch-use/ 有时候会有这样的需求,多个线程同时工作,然后其中几个可以随意并发执行,但有一个线程需要等其他线程工作结束后,才能开始。举...

balenofly
2015/06/11
0
0
CountDownLatch简单使用

CountDownLatch介绍 CountDownLatch是JAVA提供在java.util.concurrent包下的一个辅助类,可以把它看成是一个计数器,其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时...

osc_wiag8a8b
2018/05/18
2
0
Maven精选系列--classifier元素妙用

首先来看这么一个依赖 看似没问题吧?你觉得能下得下来吗?答案是否定的,下不下来。 来看看Maven的文件索引目录: Index of /maven2/net/sf/json-lib/json-lib/2.4/ 根据Maven默认组织包的结...

java技术栈
2017/11/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

主机“ xxx.xx.xxx.xxx”不允许连接到该MySQL服务器

问题: This should be dead simple, but I cannot get it to work for the life of me. 这本来应该很简单,但是我无法让它在我的一生中发挥作用。 I'm just trying to connect remotely to......

技术盛宴
今天
14
0
Cocoa Autolayout:内容拥抱与内容压缩阻力优先

问题: I can't find a clear answer on Apple documentation regarding Cocoa Autolayout about the difference between content hugging and compression resistance. 关于Cocoa Autolayou......

javail
今天
24
0
OSChina 周二乱弹 —— 附近居民接连失踪,你们有什么头绪吗

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 《伤离别(原版)》- 黄霑 手机党少年们想听歌,请使劲儿戳(这里) @巴拉迪维 :睡...

小小编辑
今天
44
0
IntelliJ IDEA 默认快捷键大全

Remember these Shortcuts 常用 功能 快捷键 备注 ● Smart code completion Ctrl + Shift + Space - ● Search everywhere Double Shift - ● Show intention actions and quick-fixes Alt......

巨輪
今天
30
0
Hacker News 简讯 2020-07-14

更新时间: 2020-07-14 04:01 Bitcoin is more like ham radio than the early internet - (jpkoning.blogspot.com) 比特币更像是火腿收音机,而不是早期的互联网 得分:159 | 评论:140 Chipma......

FalconChen
今天
136
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部