CountDownLatch是什么

CountDownLatch,英文翻译为倒计时锁存器,是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:

  • 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
  • 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
  • 等待直到某个操作所有参与者都准备就绪再继续执行。

CountDownLatch有一个正数计数器,countDown()方法对计数器做减操作,await()方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。

闭锁(倒计时锁)主要用来保证完成某个任务的先决条件满足。是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。

CountDownLatch 的两种典型用法

一:某一线程在开始运行前等待n个线程执行完毕。

将 CountDownLatch 的计数器初始化为n :new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

二:实现多个线程开始执行任务的最大并行性。

注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的 CountDownLatch 对象,将其计数器初始化为 1 :new CountDownLatch(1),多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

CountDownLatch 方法示例

方法功能:模拟项目完成--3个同事做ui,做完ui给另外2个同事做前端:
主线程
package CountDownLatchTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @Description:模拟项目完成--3个同事做ui,做完ui给另外2个同事做前端:
* 要求UI将图片全部画完之后 才能交付给前端
* @Param:
* @return:
* @Date: 2019/9/17
*/
public class CountDownLatchDemo { public static void main(String[] args) {
CountDownLatch UIcountDownLatch =new CountDownLatch(3);
CountDownLatch JScountDownLatch =new CountDownLatch(2); System.out.println("项目开始做了");
ExecutorService executor=Executors.newFixedThreadPool(5);
UIPeople uiPeople=new UIPeople(UIcountDownLatch);
JSPeople jsPeople=new JSPeople(JScountDownLatch);
try {
//3个UI开始工作
for (int i = 0; i <3 ; i++) {
executor.submit(uiPeople);
}
// 等待3个UI工作进行完成
UIcountDownLatch.await();
System.out.println("UI工作全部完成");
//2个前端开始工作
for (int i = 0; i <2 ; i++) {
executor.submit(jsPeople);
}
// 等待2个前端工作进行完成
JScountDownLatch.await();
System.out.println("前端工作全部完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("项目做完了");
}
}

UI工作人员类

package CountDownLatchTest;

import java.util.Random;
import java.util.concurrent.CountDownLatch; public class UIPeople implements Runnable {
private CountDownLatch latch; public UIPeople(CountDownLatch latch) {
this.latch = latch;
} @Override
public void run() {
try {
long start = System.currentTimeMillis();
Thread.sleep(new Random().nextInt(10)*1000);
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"界面画完了用时:"+(end-start)+"ms");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//工做做完了计时器减一
latch.countDown();
}
}
}

前端工作人员

package CountDownLatchTest;

import java.util.Random;
import java.util.concurrent.CountDownLatch; /**
* @Description: 前端人员
* @Param:
* @return:
* @Date: 2019/9/17
*/
public class JSPeople implements Runnable{ private CountDownLatch latch; public JSPeople(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
long start = System.currentTimeMillis();
Thread.sleep(new Random().nextInt(10)*1000);
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"根据图片将前端页面画完了:"+(end-start)+"ms");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//工做做完了计时器减一
latch.countDown();
}
}
}
上面示例解释

首先使用5个线程池表示5个人员,2个CountDownLatch表示前端人员和UI人员的工作,首先UI人员进行工作,因为每个人员工作时间是不一样的,但是不管他们工作时间的长短,都会停在UIcountDownLatch.await()这个方法,
等待所有的UI人员工作进行完成,而我们是怎么职到UI人员是怎么做的呢?由上面代码可知在UI成员的线程的任务里最后都执行到latch.countDown()时,在调用这个方法时主线程的计数器会减一,当主线程的countDownLatch
计数器为0时,代表所有的UI成员任务都执行成功了,然后主线程继续执行下一步,前端人员同理。

CyclicBarrier简介

CyclicBarrier,是JDK1.5的java.util.concurrent并发包中提供的一个并发工具类。

所谓Cyclic即 循环 的意思,所谓Barrier即 屏障 的意思。

所以综合起来,CyclicBarrier指的就是 循环屏障,虽然这个叫法很奇怪,但是确能很好地表示它的作用。

CyclicBarrier方法说明

——CyclicBarrier(parties)

  初始化相互等待的线程数量的构造方法。

——CyclicBarrier(parties,Runnable barrierAction)

  初始化相互等待的线程数量以及屏障线程的构造方法。

屏障线程的运行时机:等待的线程数量=parties之后,CyclicBarrier打开屏障之前。

举例:在分组计算中,每个线程负责一部分计算,最终这些线程计算结束之后,交由屏障线程进行汇总计算。

——getParties()

  获取CyclicBarrier打开屏障的线程数量,也成为方数。

——getNumberWaiting()

  获取正在CyclicBarrier上等待的线程数量。

——await()

  在CyclicBarrier上进行阻塞等待,直到发生以下情形之一:

  • 在CyclicBarrier上等待的线程数量达到parties,则所有线程被释放,继续执行。
  • 当前线程被中断,则抛出InterruptedException异常,并停止等待,继续执行。
  • 其他等待的线程被中断,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
  • 其他等待的线程超时,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
  • 其他线程调用CyclicBarrier.reset()方法,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。

——await(timeout,TimeUnit)

  在CyclicBarrier上进行限时的阻塞等待,直到发生以下情形之一:

  • 在CyclicBarrier上等待的线程数量达到parties,则所有线程被释放,继续执行。
  • 当前线程被中断,则抛出InterruptedException异常,并停止等待,继续执行。
  • 当前线程等待超时,则抛出TimeoutException异常,并停止等待,继续执行。
  • 其他等待的线程被中断,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
  • 其他等待的线程超时,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
  • 其他线程调用CyclicBarrier.reset()方法,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。

——isBroken()

  获取是否破损标志位broken的值,此值有以下几种情况:

  • CyclicBarrier初始化时,broken=false,表示屏障未破损。
  • 如果正在等待的线程被中断,则broken=true,表示屏障破损。
  • 如果正在等待的线程超时,则broken=true,表示屏障破损。
  • 如果有线程调用CyclicBarrier.reset()方法,则broken=false,表示屏障回到未破损状态。

——reset()

  使得CyclicBarrier回归初始状态,直观来看它做了两件事:

  • 如果有正在等待的线程,则会抛出BrokenBarrierException异常,且这些线程停止等待,继续执行。
  • 将是否破损标志位broken置为false。

CyclicBarrier方法练习

主线程

package CyclicBarrierTest;

import javafx.concurrent.Worker;

import java.util.concurrent.CyclicBarrier;

/**
* @Description:
* @Param:
* @return: 一个会议要等10个人都在的时候才能开始开
* @Date: 2019/9/17
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(10); for (int i = 0; i < 10; i++) {
System.out.println("开始进场" + i);
People people = new People(cyclicBarrier);
new Thread(people).start();
} }
}

人员类

package CyclicBarrierTest;

import java.util.concurrent.CyclicBarrier;

public class People implements Runnable {

    private CyclicBarrier cyclicBarrier;

    public People(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
} @Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "进成功开始等待其他人,当前有"+cyclicBarrier.getNumberWaiting()+"人在等待");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "开始开会");
// 工作线程开始处理,这里用Thread.sleep()来模拟业务处理
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "开会完毕");
} catch (Exception e) {
e.printStackTrace();
} }
}

执行结果

D:\java\jdk1.8\bin\java.exe "-javaagent:D:\ideaa\IntelliJ IDEA 2018.1.2\lib\idea_rt.jar=57236:D:\ideaa\IntelliJ IDEA 2018.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8\jre\lib\charsets.jar;D:\java\jdk1.8\jre\lib\deploy.jar;D:\java\jdk1.8\jre\lib\ext\access-bridge-32.jar;D:\java\jdk1.8\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8\jre\lib\ext\dnsns.jar;D:\java\jdk1.8\jre\lib\ext\jaccess.jar;D:\java\jdk1.8\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8\jre\lib\ext\localedata.jar;D:\java\jdk1.8\jre\lib\ext\nashorn.jar;D:\java\jdk1.8\jre\lib\ext\sunec.jar;D:\java\jdk1.8\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8\jre\lib\ext\zipfs.jar;D:\java\jdk1.8\jre\lib\javaws.jar;D:\java\jdk1.8\jre\lib\jce.jar;D:\java\jdk1.8\jre\lib\jfr.jar;D:\java\jdk1.8\jre\lib\jfxswt.jar;D:\java\jdk1.8\jre\lib\jsse.jar;D:\java\jdk1.8\jre\lib\management-agent.jar;D:\java\jdk1.8\jre\lib\plugin.jar;D:\java\jdk1.8\jre\lib\resources.jar;D:\java\jdk1.8\jre\lib\rt.jar;E:\gyf\target\classes CyclicBarrierTest.CyclicBarrierDemo
开始进场0
开始进场1
开始进场2
开始进场3
开始进场4
开始进场5
开始进场6
开始进场7
开始进场8
开始进场9
Thread-0进成功开始等待其他人,当前有0人在等待
Thread-4进成功开始等待其他人,当前有1人在等待
Thread-8进成功开始等待其他人,当前有2人在等待
Thread-1进成功开始等待其他人,当前有3人在等待
Thread-5进成功开始等待其他人,当前有4人在等待
Thread-9进成功开始等待其他人,当前有5人在等待
Thread-3进成功开始等待其他人,当前有6人在等待
Thread-7进成功开始等待其他人,当前有7人在等待
Thread-2进成功开始等待其他人,当前有8人在等待
Thread-6进成功开始等待其他人,当前有9人在等待
Thread-6开始开会
Thread-0开始开会
Thread-4开始开会
Thread-8开始开会
Thread-1开始开会
Thread-5开始开会
Thread-9开始开会
Thread-3开始开会
Thread-7开始开会
Thread-2开始开会
Thread-6开会完毕
Thread-7开会完毕
Thread-2开会完毕
Thread-4开会完毕
Thread-0开会完毕
Thread-3开会完毕
Thread-5开会完毕
Thread-8开会完毕
Thread-9开会完毕
Thread-1开会完毕 Process finished with exit code 0

上面是模拟10个人一起开会的情况,当第一个线程到最后第一个线程开始执行后,都会等待后续线程到达cyclicBarrier.await()时会停驶当前线程的任务,当所有线程执行到await时,并行开始进行下一布任务

CountDownLatch与CyclicBarrier的区别:

CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。 
CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。 
区别:

    • CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次
    • CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。
    • CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

JAVA并发工具类---------------(CountDownLatch和CyclicBarrier)的更多相关文章

  1. 25.大白话说java并发工具类-CountDownLatch,CyclicBarrier,Semaphore,Exchanger

    1. 倒计时器CountDownLatch 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join ...

  2. Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger

    本文部分摘自<Java 并发编程的艺术> CountDownLatch CountDownLatch 允许一个或多个线程等待其他线程完成操作.假设现有一个需求:我们需要解析一个 Excel ...

  3. Java并发工具类 - CountDownLatch

    Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http: ...

  4. Java并发工具类CountDownLatch源码中的例子

    Java并发工具类CountDownLatch源码中的例子 实例一 原文描述 /** * <p><b>Sample usage:</b> Here is a pai ...

  5. java 并发工具类CountDownLatch & CyclicBarrier

    一起在java1.5被引入的并发工具类还有CountDownLatch.CyclicBarrier.Semaphore.ConcurrentHashMap和BlockingQueue,它们都存在于ja ...

  6. Java中的并发工具类(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

    在JDK的并发包里提供了很多有意思的并发工具类.CountDownLatch.CyclicBarrier和Semaphore 工具类提供了一种并发流程控制的手段,Exchanger 工具类则提供了在线 ...

  7. java多线程10:并发工具类CountDownLatch、CyclicBarrier和Semaphore

    在JDK的并发包(java.util.concurrent下)中给开发者提供了几个非常有用的并发工具类,让用户不需要再去关心如何在并发场景下写出同时兼顾线程安全性与高效率的代码. 本文分别介绍Coun ...

  8. 多线程学习笔记六之并发工具类CountDownLatch和CyclicBarrier

    目录 简介 CountDownLatch 示例 实现分析 CountDownLatch与Thread.join() CyclicBarrier 实现分析 CountDownLatch和CyclicBa ...

  9. 1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask

    CountDownLatch的2个用途: 1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)[此情况需到达起跑线后再调用await()等待其他线程] 2. 所有 ...

随机推荐

  1. I2C用户态驱动设计

    一.用户态驱动模型 1.1 I2C通用驱动代码 i2c_dev_init: static int __init i2c_dev_init(void) { int res; printk(KERN_IN ...

  2. PWM输出,呼吸灯

    一.初始化GPIO 使用PB1,查芯片手册对应TIM3_CH4 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2 ...

  3. Procedure or function 'pu_usr_User' expects parameter '@WhiteIp', which was not supplied.

    遇到这个问题,是因为存储过程的参数,设置默认值写错了. 错误写法 @WhiteIp NVARCHAR(MAX) NULL, Stored procedure with default paramete ...

  4. 声明式语法重写基于容器CICD构建流水线

    调试了一下午,一句话都不想说了,看代码. ----- 参考文档 https://blog.csdn.net/weixin_42010394/article/details/90670945 实践代码 ...

  5. 96、搬家到csdn

    大家好: 今天开始会将所有的博客搬家到CSDN,以后请参考CSDN上的博客:http://blog.csdn.net/u012416045 谢谢 维真

  6. 《Java技术》 第二次作业

    java第二次作业 (一)学习总结 1.学习使用Eclipse关联jdk源代码,查看String类的equals()方法,截图,并学习其实现方法.举例说明equals方法和==的区别. 在Eclips ...

  7. NetworkComms V3 序列化器之Protobuf.net和 JSONSerializer

    NetworkComms v3版本中,默认使用的是protobuf.net序列化器. 即当您没有指定序列化的时候,系统自动使用默认的protobuf.net序列化器. 当然我们也可以自己指定序列化器 ...

  8. VIM 单词大小写转换

    遇到大小写转换的时候,我觉得首先一个不应该直接放弃的选择就是采用正则表达式以及文本替换功能.不过,针对单个单词的转换在VIM中还有更为简单的方式. 组合命令gUw可以实现把光标当前所在位置的一个单词转 ...

  9. docker-compose的scale的用法

    用到的三个文件 docker-compose version: " services: redis: image: redis web: build: context: . dockerfi ...

  10. 一份详尽的 Java 问题排查工具清单,值得收藏!

    | grep 5 -A 3    #上匹配seq 10 | grep 5 -B 3    #下匹配seq 10 | grep 5 -C 3    #上下匹配,平时用这个就妥了cat f.txt | g ...