JavaCountDownLatch的源碼硬核解析_第1頁
JavaCountDownLatch的源碼硬核解析_第2頁
JavaCountDownLatch的源碼硬核解析_第3頁
JavaCountDownLatch的源碼硬核解析_第4頁
JavaCountDownLatch的源碼硬核解析_第5頁
已閱讀5頁,還剩4頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第JavaCountDownLatch的源碼硬核解析目錄前言介紹和使用例子概述實現(xiàn)思路源碼解析類結(jié)構(gòu)圖await()實現(xiàn)原理countDown()實現(xiàn)原理

前言

對于并發(fā)執(zhí)行,Java中的CountDownLatch是一個重要的類,簡單理解,CountDownLatch中countdown是倒數(shù)的意思,latch則是門閂的含義。在數(shù)量倒數(shù)到0的時候,打開門閂,一起走,否則都等待在門閂的地方。

為了更好的理解CountDownLatch這個類,本文通過例子和源碼帶領(lǐng)大家深入解析這個類的原理。

介紹和使用

例子

我們先通過一個例子快速理解下CountDownLatch的妙處。

最近LOLS12賽如火如荼舉行,比如我們玩王者榮耀的時候,10個萬玩家登入游戲,每個玩家的網(wǎng)速可能不一樣,只有每個人進(jìn)度條走完,才會一起來到王者峽谷,網(wǎng)速快的要等網(wǎng)速慢的。我們通過例子模擬下這個過程。

@Slf4j(topic="a.CountDownLatchTest")

publicclassCountDownLatchTest{

publicstaticvoidmain(String[]args)throwsInterruptedException{

//創(chuàng)建一個倒時器,默認(rèn)10個數(shù)量

CountDownLatchlatch=newCountDownLatch(10);

ExecutorServiceservice=Executors.newFixedThreadPool(10);

//設(shè)置進(jìn)度數(shù)據(jù)

String[]personProcess=newString[10];

Randomrandom=newRandom();

for(inti=0;ii++){

intfinalJ=i;

service.submit(()-{

//模擬10個人的進(jìn)度條

for(intj=0;j=100;j++){

//模擬網(wǎng)速快慢,隨機生成

try{

Thread.sleep(random.nextInt(100));

}catch(InterruptedExceptione){

e.printStackTrace();

//設(shè)置進(jìn)度數(shù)據(jù)

personProcess[finalJ]=j+"%";

("{}",Arrays.toString(personProcess));

//運行結(jié)束,倒時器-1

latch.countDown();

//打開"閥門"

latch.await();

("王者峽谷到了");

service.shutdown();

運行結(jié)果:

概述

CountDownLatch一般用作多線程倒計時計數(shù)器,強制它們等待其他一組(CountDownLatch的初始化決定)任務(wù)執(zhí)行完成。

構(gòu)造器:

publicCountDownLatch(intcount):設(shè)置倒數(shù)器需要倒數(shù)的數(shù)量

常用API:

publicvoidawait()throwsInterruptedException:調(diào)用await()方法的線程會被掛起,等待直到count值為0再繼續(xù)執(zhí)行。publicbooleanawait(longtimeout,TimeUnitunit)throwsInterruptedException:同await(),若等待timeout時長后,count值還是沒有變?yōu)?,不再等待,繼續(xù)執(zhí)行。時間單位如下常用的毫秒、天、小時、微秒、分鐘、納秒、秒。publicvoidcountDown():count值遞減1publiclonggetCount():獲取當(dāng)前count值

常見使用場景:

一個程序中有N個任務(wù)在執(zhí)行,我們可以創(chuàng)建值為N的CountDownLatch,當(dāng)每個任務(wù)完成后,調(diào)用一下countDown()方法進(jìn)行遞減count值,再在主線程中使用await()方法等待任務(wù)執(zhí)行完成,主線程繼續(xù)執(zhí)行。

實現(xiàn)思路

通過前面的例子和介紹我們知道CountDownLatch的大致使用流程:

創(chuàng)建CountDownLatch并設(shè)置計數(shù)器值。啟動多線程并且調(diào)用CountDownLatch實例的countDown()方法。主線程調(diào)用await()方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務(wù),count值為0,停止阻塞,主線程繼續(xù)執(zhí)行。

不妨我們先思考下,它是怎么實現(xiàn)的呢?我們可以問自己幾個問題?

如何做到可以讓主線程阻塞等待在那里?是不是可以調(diào)用LockSupport.park()方法進(jìn)行阻塞。那么什么時候該阻塞呢?我們需要有個變量,比如state,如果state大于0,就阻塞主線程。那么什么時候該喚醒呢,又如何喚醒呢?如果任務(wù)執(zhí)行完成后,我們讓state減去1,也就是調(diào)用countDown()方法,如果發(fā)現(xiàn)state是0,那么就調(diào)用LockSupport.unpark()喚醒此前阻塞的地方,繼續(xù)執(zhí)行。

是不是很熟悉,這就是我們的AQS共享模式的實現(xiàn)原理啊,不了解AQS共享模式的可以參考本篇文章:深入淺出理解Java并發(fā)AQS的共享鎖模式

我們把思路理清楚后,直接看CountDownLatch的源碼。

源碼解析

類結(jié)構(gòu)圖

以上是CountDownLatch的類結(jié)構(gòu)圖,

Sync是CountDownLatch的內(nèi)部類,被成員變量sync持有。Sync繼承了AbstractQueuedSynchronizer,也就是我們大名鼎鼎的AQS。

await()實現(xiàn)原理

1.線程調(diào)用await()會阻塞等待其他線程完成任務(wù)

//CountDownLatch#await

publicvoidawait()throwsInterruptedException{

//調(diào)用AbstractQueuedSynchronizer的acquireSharedInterruptibly方法

sync.acquireSharedInterruptibly(1);

//AbstractQueuedSynchronizer#acquireSharedInterruptibly

publicfinalvoidacquireSharedInterruptibly(intarg)throwsInterruptedException{

//判斷線程是否被打斷,拋出打斷異常

if(Terrupted())

thrownewInterruptedException();

//嘗試獲取共享鎖

//條件成立說明state0,此時線程入隊阻塞等待,等待其他線程獲取共享資源

//條件不成立說明state=0,此時不需要阻塞線程,直接結(jié)束函數(shù)調(diào)用

if(tryAcquireShared(arg)0)

//阻塞當(dāng)前線程的邏輯

doAcquireSharedInterruptibly(arg);

//CountDownLatch.Sync#tryAcquireShared

protectedinttryAcquireShared(intacquires){

return(getState()==0)1:-1;

2.doAcquireSharedInterruptibly()方法是實現(xiàn)線程阻塞的核心邏輯

//AbstractQueuedSynchronizer#doAcquireSharedInterruptibly

privatevoiddoAcquireSharedInterruptibly(intarg)throwsInterruptedException{

//將調(diào)用latch.await()方法的線程包裝成SHARED類型的node加入到AQS的阻塞隊列中

finalNodenode=addWaiter(Node.SHARED);

booleanfailed=true;

try{

for(;;){

//獲取當(dāng)前節(jié)點的前驅(qū)節(jié)點

finalNodep=node.predecessor();

//前驅(qū)節(jié)點時頭節(jié)點就可以嘗試獲取鎖

if(p==head){

//再次嘗試獲取鎖,獲取成功返回1

intr=tryAcquireShared(arg);

if(r=0){

//獲取鎖成功,設(shè)置當(dāng)前節(jié)點為head節(jié)點,并且向后傳播

setHeadAndPropagate(node,r);

p.next=null;//helpGC

failed=false;

return;

//阻塞在這里

if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())

thrownewInterruptedException();

}finally{

//阻塞線程被中斷后拋出異常,進(jìn)入取消節(jié)點的邏輯

if(failed)

cancelAcquire(node);

3.parkAndCheckInterrupt()方法中會進(jìn)行阻塞操作

privatefinalbooleanparkAndCheckInterrupt(){

//阻塞線程

LockSupport.park(this);

returnTerrupted();

countDown()實現(xiàn)原理

1.任務(wù)結(jié)束調(diào)用countDown()完成計數(shù)器減一(釋放鎖)的操作

publicvoidcountDown(){

sync.releaseShared(1);

publicfinalbooleanreleaseShared(intarg){

//嘗試釋放共享鎖

if(tryReleaseShared(arg)){

//釋放鎖成功開始喚醒阻塞節(jié)點

doReleaseShared();

returntrue;

returnfalse;

2.調(diào)用tryReleaseShared()方法嘗試釋放鎖,true表示state等于0,去喚醒阻塞線程。

protectedbooleantryReleaseShared(intreleases){

for(;;){

intc=getState();

//條件成立說明前面【已經(jīng)有線程觸發(fā)喚醒操作】了,這里返回false

if(c==0)

returnfalse;

//計數(shù)器減一

intnextc=c-1;

if(compareAndSetState(c,nextc))

//計數(shù)器為0時返回true

returnnextc==0;

3.調(diào)用doReleaseShared()喚醒阻塞的節(jié)點

privatevoiddoReleaseShared(){

for(;;){

Nodeh=head;

//判斷隊列是否是空隊列

if(h!=nullh!=tail){

intws=h.waitStatus;

//頭節(jié)點的狀態(tài)為signal,說明后繼節(jié)點沒有被喚醒過

if(ws==Node.SIGNAL){

//cas設(shè)置頭節(jié)點的狀態(tài)為0,設(shè)置失敗繼續(xù)自旋

if(!compareAndSetWaitStatus(h,Node.SIGNAL,0))

continue;

//喚醒后繼節(jié)點

unparkSuccessor(h);

//如果有其他線程已經(jīng)設(shè)置了頭節(jié)點的狀態(tài),重新設(shè)置為PROPAGATE傳播屬性

elseif(w

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論