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不会阻塞主线程,只会阻塞子线程。

最新文章

  1. hdu-2444-二分图判定+最大分配
  2. angular开发单页面应用--页面资源部分
  3. [译] Paxos算法详解
  4. awk 命令
  5. c# HttpWebRequest 与 HttpWebResponse
  6. java:关于继承变量的值问题
  7. excel转化为table(去掉所有列值都为空的值一行,即返回有效值的DataTable)
  8. 《Learninghard C#学习笔记》回馈网友,免费送书5本
  9. 微信的 JSSDK
  10. WinAPI——钩子函数大全2
  11. BZOJ_2754__[SCOI2012]_喵星球上的点名_(暴力+后缀数组)
  12. c++学习笔记1(c++简介)
  13. lua wireshark 数据报解析
  14. 逆向iOS SDK -- _UIImageAtPath 的实现(SDK 5.1)
  15. 从零开始搭建Jenkins+Docker自动化集成环境
  16. BeautifulSoup练习第一节
  17. 戏说java多线程之CyclicBarrier(循环栅栏)的CyclicBarrier(int parties)构造方法
  18. 7.20 Codeforces Beta Round #8
  19. STM32F412应用开发笔记之七:片上ADC的应用测试
  20. 20155208徐子涵 2016-2017-2 《Java程序设计》第10周学习总结

热门文章

  1. Tomcat负载均衡图片显示不正常解决方法
  2. WebRequest发送请求并接收返回值
  3. Random Point in Triangle
  4. Migrating Your Android App from Eclipse to Android Studio
  5. dos添加文件夹属性!
  6. java并发编程笔记(二)——并发工具
  7. volatile在嵌入式系统中的用法
  8. nmon 定时任务 监控资源
  9. selinux与kernel 0day
  10. 实用的Python(2)利用Python制作gif动图