Java中的CountDownLatch源碼解析
一、簡(jiǎn)介
1、CountDownLatch類(lèi)是一個(gè)同步輔助裝置,允許一個(gè)或多個(gè)線(xiàn)程去等待直到另外的線(xiàn)程完成了一組操作。
2、它通過(guò)count進(jìn)行初始化,await方法會(huì)阻塞直到當(dāng)前的count為0由于調(diào)用了countDown方法,之后所有的線(xiàn)程將被釋放并且立即返回結(jié)果。count不能被重置,如果你想count可以重置,請(qǐng)使用CyclicBarrier。
3、CountDownLatch是一個(gè)通用的同步工具,可用于多種用途。CountDownLatch初始化使用count作為一個(gè)簡(jiǎn)單的可開(kāi)可關(guān)的大門(mén):所有的線(xiàn)程調(diào)用await方法等待在大門(mén)里,當(dāng)一個(gè)線(xiàn)程調(diào)用了countDown方法后大門(mén)打開(kāi)
二、源碼分析
public class CountDownLatch {
//內(nèi)部類(lèi)Sync繼承了AQS
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//構(gòu)造方法中的count其實(shí)就是傳給了AQS的state屬性
Sync(int count) {
setState(count);
}
//得到的AQS的state屬性值
int getCount() {
return getState();
}
//重寫(xiě)的AQS的tryAcquireShared,在共享模式的情況下獲取鎖
protected int tryAcquireShared(int acquires) {
//獲取鎖的前提是state為0,表示當(dāng)前未被其他線(xiàn)程占有
return (getState() == 0) ? 1 : -1;
}
//重寫(xiě)的AQS的tryReleaseShared,在共享模式下釋放鎖
protected boolean tryReleaseShared(int releases) {
// 減count; 當(dāng)count為0時(shí)喚醒
for (;;) {
int c = getState();
if (c == 0) //表示釋放鎖的前提是占有鎖,也就是state的屬性值大于0
return false;
int nextc = c-1; //state的值減1
if (compareAndSetState(c, nextc)) //利用CAS來(lái)改變state的值
return nextc == 0; //當(dāng)state的值為0時(shí)返回true
}
}
}
private final Sync sync;
/**
* 通過(guò)count初始化一個(gè)CountDownLatch
*
*在線(xiàn)程調(diào)用await方法后如果想通過(guò),必須執(zhí)行count次countDown方法
* @如果count為負(fù)數(shù),則拋出IllegalArgumentException
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
================================================================================================
兩個(gè)核心方法,其實(shí)底層使用的AQS的方法(共享模式下)
/**
* 調(diào)用await方法會(huì)使當(dāng)前的線(xiàn)程等待除非count的數(shù)值降為0或者中間拋出異常
*
* 如果當(dāng)前的count為0,則該方法立即返回
*
*如果當(dāng)前的count大于0,則當(dāng)前線(xiàn)程因線(xiàn)程調(diào)度目的而被禁用,并且休眠,直到發(fā)生以下兩種情況之一:
*1、調(diào)用countDown方法將count的值降為0
*2、其他的線(xiàn)程打斷了當(dāng)前線(xiàn)程,使用Thread.interrupted
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
*減count的值,如果count的值為0了,則釋放所有等待的線(xiàn)程
*
*如果當(dāng)前的count值大于0,那么它是被減了。
*如果當(dāng)前的count值等于0,那么所有等待的線(xiàn)程被喚醒接受線(xiàn)程的調(diào)度
*/
public void countDown() {
sync.releaseShared(1);
}
================================================================================================
/**
*返回count的值,用于調(diào)試或者測(cè)試
*/
public long getCount() {
return sync.getCount();
}三、小練習(xí)-模擬王者榮耀單挑
public static void main(String[] args) throws Exception{
CountDownLatch cd = new CountDownLatch(3);
Thread beginGame = new Thread(new Runnable() {
@Override
public void run() {
try{
cd.await();
System.out.println("歡迎來(lái)到王者榮耀,敵軍還有30秒到達(dá)戰(zhàn)場(chǎng)============");
}catch(Exception e){
e.printStackTrace();
}
}
},"beginGame");
Thread player1 = new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println("玩家一以準(zhǔn)備=======");
cd.countDown();
}catch(Exception e){
e.printStackTrace();
}
}
},"player1");
Thread player2 = new Thread(new Runnable() {
@Override
public void run() {
try{
System.out.println("玩家二以準(zhǔn)備=======");
cd.countDown();
}catch(Exception e){
e.printStackTrace();
}
}
},"player2");
beginGame.start();
player1.start();
player2.start();
}四、總結(jié)
1、請(qǐng)注意,只有在共享模式下才能使用CountDownLatch,因?yàn)橹挥性诠蚕砟J较?,AQS的state屬性的值才有可能大于1,才有后續(xù)的等待state的值為0,其他的線(xiàn)程才能被喚醒繼續(xù)執(zhí)行的可能,這里針對(duì)的多個(gè)線(xiàn)程等待!
2、CountDownLatch里面有兩個(gè)核心方法:await和countDown。這里注意,await和平時(shí)學(xué)習(xí)的Condition中的await不一樣,這里的await就是線(xiàn)程獲取鎖且必須在state值為0,也就是該資源未被占有的情況下,獲取鎖成功后state的值從0變?yōu)?,表示該線(xiàn)程持有了該資源的鎖。countDown就是將state的屬性值減1。這兩個(gè)方法其實(shí)都是調(diào)用的AQS的方法執(zhí)行的。
3、初始化傳入的count表示的就是AQS的state屬性的值,可以理解為持有該資源的線(xiàn)程數(shù),其他的線(xiàn)程想要拿到這個(gè)資源,必須等到該資源的state的值變?yōu)?才能被喚醒去獲取鎖。也說(shuō)明了如果該state的值要從N變?yōu)?,需要執(zhí)行N次countDown方法
4、有點(diǎn)類(lèi)似于線(xiàn)程通信機(jī)制的wait/notify。
5、CountDownLatch典型的用法是將一個(gè)程序分為n個(gè)互相獨(dú)立的可解決任務(wù),并創(chuàng)建值為n的CountDownLatch。當(dāng)每一個(gè)任務(wù)完成時(shí),都會(huì)在這個(gè)鎖存器上調(diào)用countDown,等待問(wèn)題被解決的任務(wù)調(diào)用這個(gè)鎖存器的await,將他們自己攔住,直至鎖存器計(jì)數(shù)結(jié)束。
6、個(gè)人理解:線(xiàn)程A和線(xiàn)程B爭(zhēng)奪一個(gè)資源,且線(xiàn)程B需要在線(xiàn)程A之后才能執(zhí)行,線(xiàn)程A首先拿到這個(gè)資源,然后線(xiàn)程A需要執(zhí)行一個(gè)大任務(wù),這個(gè)大任務(wù)可以分解為N個(gè)小任務(wù),所以,創(chuàng)建一個(gè)N的count的CountDownLatch,所以,state的值就是N了,線(xiàn)程A每執(zhí)行完一個(gè)小任務(wù)后,將調(diào)用CountDownLatch的countDown方法將state的值減1,直到最后,所有的小任務(wù)執(zhí)行完畢,代表線(xiàn)程A的大任務(wù)也就執(zhí)行完畢了,此時(shí),該資源的state的值為0了,代表可以被其他線(xiàn)程爭(zhēng)奪獲取鎖。線(xiàn)程B就可以調(diào)用countDown的await就能夠獲取該資源的鎖,并將state的值變?yōu)?。
到此這篇關(guān)于Java中的CountDownLatch源碼解析的文章就介紹到這了,更多相關(guān)CountDownLatch源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)Composite組合模式的實(shí)例代碼
這篇文章主要介紹了java實(shí)現(xiàn)Composite組合模式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Java中的Semaphore信號(hào)量使用方法代碼實(shí)例
這篇文章主要介紹了Java中的Semaphore信號(hào)量使用方法代碼實(shí)例,Semaphore是一種基于計(jì)數(shù)的信號(hào)量,它可以設(shè)定一個(gè)閾值,基于此,多個(gè)線(xiàn)程競(jìng)爭(zhēng)獲取許可信號(hào),做自己的申請(qǐng)后歸還,超過(guò)閾值后,線(xiàn)程申請(qǐng)?jiān)S可信號(hào)將會(huì)被阻塞,需要的朋友可以參考下2023-11-11
SpringCloud無(wú)法獲取Nacos中的配置文件信息解決方案
文章介紹了在項(xiàng)目啟動(dòng)時(shí)解決@Value占位符值找不到問(wèn)題的步驟,包括檢查版本號(hào)、添加依賴(lài)、檢查配置文件優(yōu)先級(jí)以及正確配置dataID2025-12-12
使用Lombok導(dǎo)致打印的tostring中缺少父類(lèi)的屬性問(wèn)題
使用Lombok時(shí),若發(fā)現(xiàn)@Data注解的@ToString不包含父類(lèi)屬性,可通過(guò)添加@ToString(callSuper=true)解決,此方法確保在生成toString()時(shí)包括父類(lèi)的屬性,有效解決只打印子類(lèi)屬性的問(wèn)題,這種做法對(duì)于需要完整信息展示的場(chǎng)景尤為重要2024-11-11

