一、CountDownLatch
1、作用
一组线程等待其他线程完成工作之后再执行,比较像加强版的join
下面演示下用法,5个线程6个扣除点,等待5个线程工作完成后,业务线程才进行自己的逻辑处理。
package cn.enjoy.controller.thread.CountDownLatch;
import cn.enjoy.controller.thread.DBPOLL.SleepTools;
import java.util.concurrent.CountDownLatch;
/**
* @ author:wangle
* @ description: countDownLatch的使用,6个初始化线程,6个扣除点,等待处理完之后业务线程和主线程才能进行处理自己的模块
* @ version:V1.0
* @ date:2020-03-21 20:52
**/
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化线程
private static class InitThread implements Runnable{
@Override
public void run(){
System.out.println("Thread:"+Thread.currentThread().getName()+"初始化工作");
latch.countDown();
//TODO 还可以继续做其他业务
}
}
//业务线程,扣除1次
private static class BusinessThread implements Runnable{
@Override
public void run(){
try{
latch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Thread:"+Thread.currentThread().getName()+"业务线程执行了");
}
}
//业务线程,扣除2次
private static class BusinessThread2 extends Thread{
@Override
public void run(){
SleepTools.ms(1);
System.out.println("Thread:"+Thread.currentThread().getName()+"初始化工作1");
latch.countDown();
SleepTools.ms(1);
System.out.println("Thread:"+Thread.currentThread().getName()+"初始化工作2");
latch.countDown();
}
}
public static void main(String[] args)throws InterruptedException{
//业务线程开始执行
new Thread(new BusinessThread()).start();
//初始化线程执行
for(int i=0;i<4;i++){
Thread thread = new Thread(new InitThread());
thread.start();
}
Thread thread = new BusinessThread2();
thread.start();
latch.await();
System.out.println("主线程在执行了");
}
}
看到,当5个线程扣除6次之后,主线程和业务线程才开始执行,因为主线程和业务线程执行了await操作,在等待初始化线程扣除操作完成。
二、CyclicBarrier
1、作用
一组线程到达了某个屏障,当这组线程全部到达了屏障点后全部线程才继续向下执行
package cn.enjoy.controller.thread.UseCyclicBarrier;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
/**
* @author:wangle
* @description:使用CyclicBarrier
* @version:V1.0
* @date:2020-03-21 21:28
**/
public class UseCyclicBarrier {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new BusinessThread());
private static HashMap<String,Long> threadIdMap = new HashMap<>();
private static class WorkThread extends Thread{
@Override
public void run(){
long id = Thread.currentThread().getId();
threadIdMap.put(Thread.currentThread().getId()+"",id);
Random r = new Random();
try{
if(r.nextBoolean()){
Thread.sleep(2000);
System.out.println("Thread_"+id+"还没到await,在做其他事情");
}
System.out.println("Thread_"+id+"在await");
cyclicBarrier.await();
System.out.println("Thread_"+id+"业务代码执行");
}catch (Exception e){
e.printStackTrace();
}
}
}
private static class BusinessThread extends Thread{
@Override
public void run(){
System.out.println(threadIdMap);
}
}
public static void main(String[] args){
for(int i=0;i<5;i++){
Thread thread = new WorkThread();
thread.start();
}
}
}
可以看到,线程12,11很快到达了await,但是14,13,15还没有到达await,,此时12,11并没有进行继续操作,而是等待剩余的线程全部到达await之后才进行await之后的逻辑处理。有兴趣的小伙伴可以执行下程序,明显的感受下程序堵塞等待的顺序。
CyclicBarrier还接收了一个线程的参数,这个线程也是当所有线程都到达了屏障之后才进行继续操作。这个参数可有可无,根据自己的程序需要进行传递。
三、CountDownLatch和cyclicBarrier辨析
1、CountDownLatch放行由外部线程决定,cyclicBarrier放行是由一组线程本身决定的
2、CountDownLatch的放行条件执行数大于等于线程数、cyclicBarrier的放行条件执行数等于线程数量