package com.xbz.thread.juc.countDownLatch;
import java.util.concurrent.CountDownLatch;
/**
* @title CountDownLatch類的應用
* CountDownLatch類位於java.util.concurrent(簡稱juc)包下 , 它是一個同步工具類 , 利用它可以實現類似計數器的功能
* 比如有一個任務A , 它要等待其他4個任務結束執行之後才能執行 , 此時就可以利用CountDownLatch來實現這種功能了
* 主要用來協調多個線程之間的同步 , 或者說起到線程之間的通信 ( 而不是用作互斥的作用 )
* CountDownLatch能夠使一個線程在等待另外一些線程完成各自工作之後 , 再繼續執行 . 使用一個計數器進行實現 .
* 計數器初始值爲線程的數量 .
* 當每一個線程完成自己任務後 , 計數器的值就會減一(手動) .
* 當計數器的值爲0時 , 表示所有的線程都已經完成了任務 , 然後在CountDownLatch上等待的線程就可以恢復執行任務
*
* CountDownLatch類只提供了一個構造器 :
* public CountDownLatch(int count)//參數count爲計數值 , 即需要先執行幾個線程
*
* CountDownLatch類的主要方法 :
* void await()//調用await()方法的線程會被掛起 , 它會等待直到count值爲0才繼續執行
* boolean await(long timeout, TimeUnit unit)//和await()類似 , 只不過等待一定的時間後count值還沒變爲0的話也會繼續執行
* void countDown()//將count值減1
* CountDownLatch的不足
* CountDownLatch是一次性的 , 計數器的值只能在構造方法中初始化一次 , 之後沒有任何機制再次對其設置值 , 當CountDownLatch使用完畢後 , 它不能再次被使用
* @author Xingbz
* @createDate 2018-7-18
*/
public class MainDemo {
public static void main(String[] args) throws Exception {
// demo1();
demo2();
}
/**
* @title CountDownLatch典型用法1
* @description 某一線程在開始運行前等待n個線程結束執行 .
* 將CountDownLatch的計數器初始化爲n new CountDownLatch(n) ,
* 每當一個任務線程結束執行 , 就將計數器減1 countdownlatch.countDown() ,
* 當計數器的值變爲0時 , 在CountDownLatch上 await() 的線程就會被喚醒 .
* 一個典型應用場景就是啓動一個服務時 , 主線程需要等待多個組件加載完畢 , 之後再繼續執行 .
* @author Xingbz
*/
public static void demo1() throws Exception {
// final CountDownLatch latch = new CountDownLatch(2);//2個線程後開始執行
final CountDownLatch latch = new CountDownLatch(2) {//此處是爲了演示線程過程特意重寫了await方法, 實際應用如上聲明即可
@Override
public void await() throws InterruptedException {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入阻塞狀態 . 當前計數器 : " + this.getCount());
super.await();
}
};//2個線程後開始執行
runThread("線程1", 2000L, latch);
runThread("線程2", 3000L, latch);
System.out.println("等待2個子線程結束執行...");
latch.await();//當前線程掛起 , 直到計數器爲0
// latch.await(20, TimeUnit.SECONDS);//最多阻塞20S , 20S後即使計數器不是0也繼續往下執行
System.out.println("2個子線程已經結束執行");
System.out.println("繼續執行主線程");
}
/**
* @title CountDownLatch典型用法2
* @description
* 實現多個線程開始執行任務的最大並行性 .
* 注意是並行性 , 不是併發 , 強調的是多個線程在某一時刻同時開始執行 .
* 類似於賽跑 , 將多個線程放到起點 , 等待發令槍響 , 然後同時開跑 .
* 做法是額外初始化一個共享的CountDownLatch(1) , 將其計數器初始化爲1 ,
* 多個線程在開始執行任務前首先 coundownlatch.await() , 當主線程調用 countDown() 時 , 計數器變爲0 , 多個線程同時被喚醒
* @author Xingbz
*/
public static void demo2() throws Exception {
CountDownLatch countDown = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(5);
for (int i = 0; i < 5; ++i) {// 依次創建並啓動處於等待狀態的5個線程
runThread("線程" + (i + 1), 2000L, countDown, latch);
}
System.out.println("執行了5個線程並進入了阻塞狀態 . 等待統一啓動 . . . ");
countDown.countDown();
System.out.println("主控器結束計數 , 所有線程同時開始執行 . . .");
latch.await();
System.out.println("完成");
}
/**
* @title 模擬啓動一個子線程工作
* @param threadName 線程名稱
* @param sleepTime 休眠時間
* @param countDown 主控制器 CountDownLatch 負責所有線程啓動後先進入阻塞 "預備" 狀態
* @param latch 輔控制器 CountDownLatch 負責所有線程執行完成後繼續運行主線程
* @author Xingbz
*/
private static void runThread(String threadName, Long sleepTime, CountDownLatch countDown, CountDownLatch latch) {
Thread thread = new Thread(() -> {
try {
System.out.println("子線程[" + Thread.currentThread().getName() + "]準備執行");
countDown.await();
Thread.sleep(sleepTime);
System.out.println("子線程[" + Thread.currentThread().getName() + "]正在執行");
Thread.sleep(sleepTime);
latch.countDown();//子線程結束執行 , latch計數器減1
System.out.println("子線程[" + Thread.currentThread().getName() + "]結束執行 , latch計數器-1 . 當前計數器 : " + latch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadName);
thread.start();
}
/**
* @title 模擬啓動一個子線程工作
* @param threadName 線程名稱
* @param sleepTime 休眠時間
* @param latch CountDownLatch
* @author Xingbz
*/
private static void runThread(String threadName, Long sleepTime, CountDownLatch latch) {
Thread thread = new Thread(() -> {
try {
System.out.println("子線程[" + Thread.currentThread().getName() + "]正在執行");
Thread.sleep(sleepTime);
latch.countDown();//子線程結束執行 , latch計數器減1
System.out.println("子線程[" + Thread.currentThread().getName() + "]結束執行 , latch計數器-1 . 當前計數器 : " + latch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadName);
thread.start();
}
}