一、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的放行條件執行數等於線程數量