第二次寫簡(jiǎn)書。還望簡(jiǎn)友多多關(guān)照。AbstractQueuedSynchronizer 類如其名:抽象隊(duì)列同步器。此類是多線程定義的共享同步容器,很多實(shí)現(xiàn)同步的方式都是在這里面實(shí)現(xiàn)的,除非一些不通用的功能需要子類去實(shí)現(xiàn),比如我們常見(jiàn)的ReentrantLock,Semaphore,CountDownLatch 等等。下面將詳細(xì)講解AQS所實(shí)現(xiàn)的功能有哪些。
首先,我們要先了解清楚什么是多線程隊(duì)列,所謂隊(duì)列就是存放元素的東西,接下來(lái)應(yīng)該就知道我要說(shuō)什么了吧?沒(méi)錯(cuò),AQS存放多個(gè)線程的容器就是所謂的CLH隊(duì)列,但是AQS并沒(méi)有采用任何Queue,而是自己實(shí)現(xiàn)了一套CLH隊(duì)列,首先我們從AQS核心Node節(jié)點(diǎn)開(kāi)始說(shuō)起。
這個(gè)Node節(jié)點(diǎn)包含了4種狀態(tài):
CANCELLED:因?yàn)槌瑫r(shí)或者中斷,結(jié)點(diǎn)會(huì)被設(shè)置為取消狀態(tài),被取消狀態(tài)的結(jié)點(diǎn)不應(yīng)該去競(jìng)爭(zhēng)鎖,只能保持取消狀態(tài)不變,不能轉(zhuǎn)換為其他狀態(tài)。處于這種狀態(tài)的結(jié)點(diǎn)會(huì)被踢出隊(duì)列,被GC回收;
SIGNAL:表示這個(gè)結(jié)點(diǎn)的繼任結(jié)點(diǎn)被阻塞了,到時(shí)需要通知它;
CONDITION:表示這個(gè)結(jié)點(diǎn)在條件隊(duì)列中,因?yàn)榈却硞€(gè)條件而被阻塞;
PROPAGATE:使用在共享模式頭結(jié)點(diǎn)有可能牌處于這種狀態(tài),表示鎖的下一次獲取可以無(wú)條件傳播;
好了,了解了Node節(jié)點(diǎn)的幾種狀態(tài)之后,我們就可以開(kāi)始研究獲取鎖源碼啦。
acquire方法傳入的參數(shù)我們先不看,因?yàn)檫@個(gè)參數(shù)是通過(guò)子類傳過(guò)來(lái)的,好,我們繼續(xù)看 tryAcquire 點(diǎn)進(jìn)去 如下圖
發(fā)現(xiàn)竟然拋異常了,意思就是AQS并不會(huì)實(shí)現(xiàn)嘗試獲取的具體功能,而是要通過(guò)子類去實(shí)現(xiàn)。我們繼續(xù)看 addWaiter方法
名副其實(shí),就是添加等待線程到隊(duì)列。先拿到隊(duì)列的尾節(jié)點(diǎn),如果尾節(jié)點(diǎn)不為空就把當(dāng)前節(jié)點(diǎn)掛在尾節(jié)點(diǎn)上,并嘗試一次去設(shè)置當(dāng)前節(jié)點(diǎn)為尾節(jié)點(diǎn),如果設(shè)置成功就返回當(dāng)前節(jié)點(diǎn),否則就enq(node),enq做了什么事?
我們發(fā)現(xiàn),這個(gè)enq所做的事無(wú)非就是自旋設(shè)置當(dāng)前節(jié)點(diǎn)為尾節(jié)點(diǎn)。OK,tryAcquire()和addWaiter(),當(dāng)前線程獲取鎖失敗,被放入隊(duì)列尾部了。放入尾部了接下來(lái)又做什么?就要看acquireQueued做了什么了,截圖:
這里一樣有一個(gè)自旋操作,先獲取當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn),如果上一個(gè)節(jié)點(diǎn)是頭節(jié)點(diǎn)并且當(dāng)前節(jié)點(diǎn)獲取到了獨(dú)占鎖,那么就將頭節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn),然后返回當(dāng)前線程睡眠狀態(tài),那么如果不是頭節(jié)點(diǎn)呢? 那么肯定會(huì)去嘗試睡眠當(dāng)前線程,那就是shouldParkAfterFailedAcquire?
如果上一個(gè)節(jié)點(diǎn)是阻塞狀態(tài),那么當(dāng)前這個(gè)節(jié)點(diǎn)就更要阻塞了,所以就可以直接返回true。如果上一個(gè)節(jié)點(diǎn)的狀被取消或中斷了,那么就會(huì)一直往上一個(gè)節(jié)點(diǎn)的前N個(gè)節(jié)點(diǎn)依次找,直到找到?jīng)]有被中斷或者取消的線程。
上一部分就大概介紹了AQS的加鎖操作。下文將分析釋放鎖操作,相對(duì)于加鎖操作要簡(jiǎn)單得多。
release方法:
先去嘗試釋放鎖,但是釋放鎖都是由同步容器去實(shí)現(xiàn),AQS本身沒(méi)有做任何事情,如果完全釋放成功,unpark喚醒下一個(gè)節(jié)點(diǎn)
node一般為當(dāng)前線程所在的結(jié)點(diǎn),置零當(dāng)前線程所在的結(jié)點(diǎn)狀態(tài),允許失敗。 然后找到下一節(jié)點(diǎn),如果下一節(jié)點(diǎn)為空或已經(jīng)取消就依次往前尋找,最后釋放找到的節(jié)點(diǎn)線程 。
OK,整個(gè)AQS涉及到的加鎖和釋放鎖已落下帷幕,希望本文能對(duì)并發(fā)編程愛(ài)好者有些許幫助,如果有收獲就點(diǎn)擊喜歡關(guān)注一下吧~~