本篇不寫前言,直接扒衣服!
1:concurrent包結(jié)構(gòu)
最底層:
volatile變量:volatile保證變量在內(nèi)存中的可見(jiàn)性。java線程模型包括線程的私有內(nèi)存和所有線程的公共內(nèi)存,這與cpu緩存和內(nèi)存類似。線程對(duì)變量進(jìn)行操作的時(shí)候一般是從公共內(nèi)存中拷貝變量的副本,等修改完之后重新寫入到公共內(nèi)存,這存在并發(fā)風(fēng)險(xiǎn)。而被volatile標(biāo)注的變量通過(guò)CPU原語(yǔ)保證了變量的內(nèi)存可見(jiàn)性,也就是說(shuō)一個(gè)線程讀到工作內(nèi)存中的變量一定是其他線程已經(jīng)更新到內(nèi)存中的變量。為簡(jiǎn)單起見(jiàn),你可以想象成所有線程都在公共內(nèi)存中操作volatile變量,這樣一個(gè)線程對(duì)volatile的更改其他線程都看的見(jiàn),即內(nèi)存可見(jiàn)性!
CAS:compare and swap,比較-相等-替換新值,跟底層CPU有關(guān),一般CAS操作的都是volatile變量。CAS操作包含內(nèi)存位置(V)、預(yù)期原值(A)和新值(B),?如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值 。否則,處理器不做任何操作。如方法a.CAS(b,c),如果內(nèi)存中a與預(yù)期值b相等,那么把a(bǔ)更新成c。cas操作在java中由sun.misc.Unsafe類全權(quán)代理了!
最底層結(jié):concurrent包中用cas死循環(huán)修改volatile變量直到修改成功是最常見(jiàn)的手法!
第二層:
AtomicXXX類:常用基本類型和引用都有Atomic實(shí)現(xiàn),其中最重要的兩點(diǎn)當(dāng)然是一個(gè)volatile變量和一個(gè)CAS操作。++i和i++操作就是volatile和cas的典型用法,incrementAndget和getAndincrement是兩個(gè)實(shí)現(xiàn)方法,死循環(huán):首先獲取volatile變量值a,然后執(zhí)行a.cas(a,a+1)直到修改成功。
AQS框架:總有面試官會(huì)問(wèn)你AQS框架,盡管我懷疑他們并不真正了解所有細(xì)節(jié),Doug lea最屌的思想應(yīng)該都在AQS框架里了,直接上圖(借用,侵刪)
該類最主要的兩部分就是state狀態(tài)和Node節(jié)點(diǎn)(即隊(duì)列中的元素),前一個(gè)是資源狀態(tài),后一個(gè)還是cas操作volatile的典型應(yīng)用,Node還分為獨(dú)占模式和共享模式
第二層源碼解析:
以獨(dú)占模式為例,想象醫(yī)院某診室排隊(duì)看病。
A:如何競(jìng)爭(zhēng)資源(去看病)?
public final void acquire(int arg) {
? ? ? ? if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
? ? ? ? ? ? selfInterrupt();
}
這是競(jìng)爭(zhēng)資源的入口,不管有多少線程、是富得流油還是窮的吃土都要從這進(jìn)!
B:獲取資源后發(fā)生了啥?
首先,嘗試獲取資源,成true敗false,此方法每個(gè)子類有自己的實(shí)現(xiàn),就像有的醫(yī)院需要排隊(duì)(公平鎖),有的可以花錢不用排隊(duì)(非公平鎖)。
protected boolean tryAcquire(int arg) {
? ? ? ? throw new UnsupportedOperationException();
}
然后,為當(dāng)前線程創(chuàng)建節(jié)點(diǎn)Node并設(shè)置成獨(dú)占模式(相當(dāng)于每個(gè)看病的人取了一個(gè)號(hào),并且是獨(dú)占診室的那種號(hào)),先用快速方法加入隊(duì)尾,加入失敗的話調(diào)用enq方法死循環(huán)加入,總之要保證每個(gè)病人都取到了號(hào),并且進(jìn)入排隊(duì)狀態(tài)。
private Node addWaiter(Node mode) {
? ? ? ? Node node = new Node(Thread.currentThread(), mode);
? ? ? ? // 快速加入隊(duì)尾,如果失敗就調(diào)用enq方法死循環(huán)加入
? ? ? ? Node pred = tail;
? ? ? ? if (pred != null) {
? ? ? ? ? ? node.prev = pred;
? ? ? ? ? ? if (compareAndSetTail(pred, node)) {
? ? ? ? ? ? ? ? pred.next = node;
? ? ? ? ? ? ? ? return node;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? enq(node);
? ? ? ? return node;
? ? }
private Node enq(final Node node) {
? ? ? ? //CAS外加死循環(huán)操作volatile的Node節(jié)點(diǎn),這個(gè)套路我們太熟悉了,保證了隊(duì)尾競(jìng)爭(zhēng)的線程安全
? ? ? ? for (;;) {
? ? ? ? ? ? Node t = tail;
? ? ? ? ? ? if (t == null) { // Must initialize
? ? ? ? ? ? ? ? if (compareAndSetHead(new Node()))
? ? ? ? ? ? ? ? ? ? tail = head;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? node.prev = t;
? ? ? ? ? ? ? ? if (compareAndSetTail(t, node)) {
? ? ? ? ? ? ? ? ? ? t.next = node;
? ? ? ? ? ? ? ? ? ? return t;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
再次,線程不能一直等待,需要休息,于是線程輪詢查看前驅(qū)節(jié)點(diǎn)是否占用了資源,如果是那么自己嘗試獲取資源,獲取到了就把自己設(shè)置為頭結(jié)點(diǎn)。如果不是,那么沒(méi)關(guān)系,調(diào)用shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()兩個(gè)方法。
final boolean acquireQueued(final Node node, int arg) {
? ? ? ? boolean failed = true;
? ? ? ? try {
? ? ? ? ? ? boolean interrupted = false;
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? final Node p = node.predecessor();
? ? ? ? ? ? ? ? if (p == head && tryAcquire(arg)) {
? ? ? ? ? ? ? ? ? ? setHead(node);
? ? ? ? ? ? ? ? ? ? p.next = null; // 幫助垃圾回收
? ? ? ? ? ? ? ? ? ? failed = false;
? ? ? ? ? ? ? ? ? ? return interrupted;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
? ? ? ? ? ? ? ? ? ? interrupted = true;
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? if (failed)
? ? ? ? ? ? ? ? cancelAcquire(node);
? ? ? ? }
? ? }
然后,假如當(dāng)前線程前驅(qū)節(jié)點(diǎn)沒(méi)有占用資源或者人家占用資源還沒(méi)釋放,那么調(diào)用shouldParkAfterFailedAcquire(p, node)方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
? ? ? ? int ws = pred.waitStatus;
? ? ? ? if (ws == Node.SIGNAL)
? ? ? ? ? ? return true;
? ? ? ? if (ws > 0) {
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? node.prev = pred = pred.prev;
? ? ? ? ? ? } while (pred.waitStatus > 0);
? ? ? ? ? ? pred.next = node;
? ? ? ? } else {
? ? ? ? ? ? compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
? ? ? ? }
? ? ? ? return false;
? ? }
看上面代碼,ws是當(dāng)前線程前驅(qū)節(jié)點(diǎn)的狀態(tài),如果前驅(qū)節(jié)點(diǎn)狀態(tài)是SIGNAL,意思是后繼節(jié)點(diǎn)應(yīng)該等待,那么返回true。否則,如果前驅(qū)節(jié)點(diǎn)狀態(tài)大于0,意思就是取消了(不排隊(duì)了)那么就移動(dòng)到不排隊(duì)的節(jié)點(diǎn)前面去,不排隊(duì)的線程最后會(huì)被垃圾回收器回收。如果前驅(qū)節(jié)點(diǎn)狀態(tài)是其他,那么就把前驅(qū)節(jié)點(diǎn)狀態(tài)設(shè)置成SIGNAL,告訴他當(dāng)前線程需要被等待。以上過(guò)程執(zhí)行完當(dāng)前線程就可以等待了parkAndCheckInterrupt()(跟前面排隊(duì)的病人溝通,告訴他我要休息一下,快叫到我的時(shí)候通知我一下)。
private final boolean parkAndCheckInterrupt() {
? ? ? ? LockSupport.park(this);
? ? ? ? return Thread.interrupted();
? ? }
最后,可見(jiàn)這里面的等待實(shí)際上是調(diào)用LockSupport.park方法實(shí)現(xiàn)的,這里的park實(shí)際上與wait方法類似。
C:有借有還,再借不難,釋放資源
public final boolean release(int arg) {
? ? ? ? if (tryRelease(arg)) {
? ? ? ? ? ? Node h = head;
? ? ? ? ? ? if (h != null && h.waitStatus != 0)
? ? ? ? ? ? ? ? unparkSuccessor(h);
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return false;
? ? }
首先,釋放資源入口是release方法,release實(shí)際上與acquire恰好鏡像,如tryRelease可以自己實(shí)現(xiàn),然后真正釋放的時(shí)候?qū)嶋H上調(diào)用了unparkSuccessor(h)方法
private void unparkSuccessor(Node node) {
? ? ? ? int ws = node.waitStatus;
? ? ? ? if (ws < 0)
? ? ? ? ? ? compareAndSetWaitStatus(node, ws, 0);
? ? ? ? Node s = node.next;
? ? ? ? if (s == null || s.waitStatus > 0) {
? ? ? ? ? ? s = null;
? ? ? ? ? ? for (Node t = tail; t != null && t != node; t = t.prev)
? ? ? ? ? ? ? ? if (t.waitStatus <= 0)
? ? ? ? ? ? ? ? ? ? s = t;
? ? ? ? }
? ? ? ? if (s != null)
? ? ? ? ? ? LockSupport.unpark(s.thread);
? ? }
解除占用方法,可以看到,首先將占用資源的線程節(jié)點(diǎn)狀態(tài)由設(shè)置成0,然后調(diào)用LockSupport.unpark方法喚醒next節(jié)點(diǎn)的線程,相當(dāng)于await/signal方法中的signal。
獨(dú)占模式說(shuō)完下面分析下共享模式。
A:如何競(jìng)爭(zhēng)資源(想想排隊(duì)上廁所)?
猜也能猜個(gè)大概,肯定是acquireShare巴拉巴拉的。
public final void acquireShared(int arg) {
? ? ? ? if (tryAcquireShared(arg) < 0)
? ? ? ? ? ? doAcquireShared(arg);
? ? }
上面代碼是獲取共享資源的,首先嘗試獲取,這個(gè)與獨(dú)占模式類似,稍有不同的地方是其返回值,-1表示獲取失敗,0表示獲取成功但沒(méi)有可用資源,1表示獲取成功并且有資源。-1表示廁所門都沒(méi)進(jìn)去、0表示進(jìn)廁所門了但是沒(méi)有坑位、1表示即進(jìn)入了廁所門又有坑位。
重點(diǎn):共享資源可以有多份,這樣可以同時(shí)讓多個(gè)線程共享資源,所以PROPAGATE狀態(tài)可以保證在一個(gè)線程釋放資源后其他狀態(tài)為PROPAGATE的線程都能被喚醒。
B:怎么競(jìng)爭(zhēng)資源?
private void doAcquireShared(int arg) {
? ? ? ? final Node node = addWaiter(Node.SHARED);
? ? ? ? boolean failed = true;
? ? ? ? try {
? ? ? ? ? ? boolean interrupted = false;
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? final Node p = node.predecessor();
? ? ? ? ? ? ? ? if (p == head) {
? ? ? ? ? ? ? ? ? ? int r = tryAcquireShared(arg);
? ? ? ? ? ? ? ? ? ? if (r >= 0) {
? ? ? ? ? ? ? ? ? ? ? ? setHeadAndPropagate(node, r);
? ? ? ? ? ? ? ? ? ? ? ? p.next = null; // help GC
? ? ? ? ? ? ? ? ? ? ? ? if (interrupted)
? ? ? ? ? ? ? ? ? ? ? ? ? ? selfInterrupt();
? ? ? ? ? ? ? ? ? ? ? ? failed = false;
? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ? parkAndCheckInterrupt())
? ? ? ? ? ? ? ? ? ? interrupted = true;
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? if (failed)
? ? ? ? ? ? ? ? cancelAcquire(node);
? ? ? ? }
? ? }
上述代碼在嘗試獲取資源失敗后進(jìn)入(即沒(méi)進(jìn)入廁所門就進(jìn)入該代碼塊)。addWaiter獨(dú)占模式中已經(jīng)講過(guò)了,先快速入隊(duì),快速失敗后enq死循環(huán)cas操作隊(duì)尾入隊(duì),只不過(guò)這里設(shè)置的模式使共享而已。然后看死循環(huán)里面的內(nèi)容,是不是太熟悉了?過(guò)程實(shí)際上是一樣的,先看下排在自己前面的人是不是占著坑位,如果是就嘗試獲取下資源,如果失敗就告訴前面排隊(duì)的你的下一位是我,釋放之后通知我,我要去等待了(實(shí)際上就是把前驅(qū)節(jié)點(diǎn)狀態(tài)設(shè)置成SIGNAL)。
private void setHeadAndPropagate(Node node, int propagate) {
? ? ? ? Node h = head; // Record old head for check below
? ? ? ? setHead(node);
? ? ? ? if (propagate > 0 || h == null || h.waitStatus < 0 ||
? ? ? ? ? ? (h = head) == null || h.waitStatus < 0) {
? ? ? ? ? ? Node s = node.next;
? ? ? ? ? ? if (s == null || s.isShared())
? ? ? ? ? ? ? ? doReleaseShared();
? ? ? ? }
? ? }
注意上面代碼與獨(dú)占模式的小區(qū)別,setHeadAndPropagate()執(zhí)行的時(shí)候表示當(dāng)前節(jié)點(diǎn)線程已經(jīng)獲取到了資源。此時(shí)它把自己設(shè)置為頭結(jié)點(diǎn),并需要喚醒其后繼節(jié)點(diǎn)(如果有的話),喚醒過(guò)程實(shí)際上調(diào)用了doReleaseShared方法。
C:有借有還,再借不難,釋放資源
public final boolean releaseShared(int arg) {
? ? ? ? if (tryReleaseShared(arg)) {
? ? ? ? ? ? doReleaseShared();
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return false;
? ? }
ok,套路與獨(dú)占模式相同,先嘗試釋放(子類自己實(shí)現(xiàn)),然后執(zhí)行釋放
private void doReleaseShared() {
? ? ? ? for (;;) {
? ? ? ? ? ? Node h = head;
? ? ? ? ? ? if (h != null && h != tail) {
? ? ? ? ? ? ? ? int ws = h.waitStatus;
? ? ? ? ? ? ? ? if (ws == Node.SIGNAL) {
? ? ? ? ? ? ? ? ? ? if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
? ? ? ? ? ? ? ? ? ? ? ? continue;? ? ? ? ? ? // loop to recheck cases
? ? ? ? ? ? ? ? ? ? unparkSuccessor(h);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
? ? ? ? ? ? ? ? ? ? continue;? ? ? ? ? ? ? ? // loop on failed CAS
? ? ? ? ? ? }
? ? ? ? ? ? if (h == head)? ? ? ? ? ? ? ? ? // loop if head changed
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? }
上述代碼是共享模式下釋放資源的實(shí)質(zhì)代碼。首先查看后繼線程是否需要被喚醒,如果需要那么執(zhí)行喚醒,讓它過(guò)來(lái)?yè)屨假Y源,然后會(huì)把自身狀態(tài)設(shè)置成PROPAGATE保證傳播喚醒。
非阻塞數(shù)據(jù)結(jié)構(gòu):這一層的各種數(shù)據(jù)結(jié)構(gòu)如concurrentHashMap,LinkedBlockingQueue咱們后續(xù)再寫,先趁熱打鐵把AQS的子子孫孫們扒個(gè)精光!
最頂層:
Lock:頂層接口,里面有l(wèi)ock(),unlock(),tryLock()等重要方法需要子類實(shí)現(xiàn)
Condition:頂層接口,里面有await(),signal(),signalAll()等重要方法類似于wait/notify等待通知模型。
ReadWriteLock:頂層接口,兩個(gè)方法,readLock()和writeLock()分別獲取讀鎖和寫鎖。
ReentrantLock類:
該類包含了Sync和其子類FairSync、NonfairSync,前者是公平鎖,后者是非公平鎖。默認(rèn)構(gòu)造函數(shù)是非公平鎖,可以通過(guò)boolean值構(gòu)造公平鎖。既然是一個(gè)鎖,那么最重要的兩個(gè)方法當(dāng)然是關(guān)鎖和開鎖,該類中對(duì)開鎖和關(guān)鎖分別實(shí)現(xiàn)了公平和非公平兩個(gè)方法,我們來(lái)看下具體實(shí)現(xiàn):
非公平鎖關(guān)鎖的實(shí)現(xiàn):
final void lock() {
????if (compareAndSetState(0, 1))
????????setExclusiveOwnerThread(Thread.currentThread());
????else
????????acquire(1);
}
public final void acquire(int arg) {
????if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
????????selfInterrupt();
}
final boolean nonfairTryAcquire(int acquires) {
? ? ? ? ? ? final Thread current = Thread.currentThread();
? ? ? ? ? ? int c = getState();
? ? ? ? ? ? if (c == 0) {
? ? ? ? ? ? ? ? if (compareAndSetState(0, acquires)) {
? ? ? ? ? ? ? ? ? ? setExclusiveOwnerThread(current);
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else if (current == getExclusiveOwnerThread()) {
? ? ? ? ? ? ? ? int nextc = c + acquires;
? ? ? ? ? ? ? ? if (nextc < 0) // overflow
? ? ? ? ? ? ? ? ? ? throw new Error("Maximum lock count exceeded");
? ? ? ? ? ? ? ? setState(nextc);
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return false;
? ? ? ? }
上述代碼是非公平鎖的實(shí)現(xiàn)方式。先快速占有state資源,如果沒(méi)占有成功再調(diào)用aquire去競(jìng)爭(zhēng),競(jìng)爭(zhēng)過(guò)程又先調(diào)用了自己實(shí)現(xiàn)的tryAcquire方法:”
用當(dāng)前線程獲取AQS中的資源state,我們前文說(shuō)過(guò),state等于0表示沒(méi)有線程占用該資源,所以,這里設(shè)置獨(dú)占,而如果發(fā)現(xiàn)當(dāng)前線程已經(jīng)占用了資源(state>0并且current==owner)那么就在state基礎(chǔ)上加上獲取的次數(shù)1。
非公平鎖開鎖:
protected final boolean tryRelease(int releases) {
? ? ? ? ? ? int c = getState() - releases;
? ? ? ? ? ? if (Thread.currentThread() != getExclusiveOwnerThread())
? ? ? ? ? ? ? ? throw new IllegalMonitorStateException();
? ? ? ? ? ? boolean free = false;
? ? ? ? ? ? if (c == 0) {
? ? ? ? ? ? ? ? free = true;
? ? ? ? ? ? ? ? setExclusiveOwnerThread(null);
? ? ? ? ? ? }
? ? ? ? ? ? setState(c);
? ? ? ? ? ? return free;
? ? ? ? }
上述代碼是非公平鎖解鎖過(guò)程,之前state被重入了多少次這里就需要釋放多少次,并且把當(dāng)前占有資源的線程設(shè)置為null!直接退出就行。
公平鎖關(guān)鎖過(guò)程:
final void lock() {? ?
????acquire(1);
}
public final void acquire(int arg) {???
????if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))????
????????selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
? ? ? ? ? ? final Thread current = Thread.currentThread();
? ? ? ? ? ? int c = getState();
? ? ? ? ? ? if (c == 0) {
? ? ? ? ? ? ? ? if (!hasQueuedPredecessors() &&
? ? ? ? ? ? ? ? ? ? compareAndSetState(0, acquires)) {
? ? ? ? ? ? ? ? ? ? setExclusiveOwnerThread(current);
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else if (current == getExclusiveOwnerThread()) {
? ? ? ? ? ? ? ? int nextc = c + acquires;
? ? ? ? ? ? ? ? if (nextc < 0)
? ? ? ? ? ? ? ? ? ? throw new Error("Maximum lock count exceeded");
? ? ? ? ? ? ? ? setState(nextc);
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return false;
? ? ? ? }
上述代碼是公平鎖實(shí)現(xiàn)過(guò)程,重點(diǎn)在?if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) 這個(gè)hasQueuePredescessors方法!只有這個(gè)地方公平鎖與非公平鎖存在差異,該方法顧名思義,判斷當(dāng)前線程節(jié)點(diǎn)是否有前驅(qū)節(jié)點(diǎn),如果沒(méi)有才會(huì)獨(dú)占資源。
公平鎖的釋放過(guò)程與非公平鎖相同!
那么公平性與非公平性體現(xiàn)在哪??jī)蓚€(gè)方面:
A:非公平鎖在lock()的時(shí)候直接用當(dāng)前線程占用資源失敗之后才會(huì)調(diào)用acquire方法,這是其一
B:非公平鎖在acquire方法調(diào)用過(guò)程中調(diào)用了自己實(shí)現(xiàn)的tryAcquire方法,該方法不會(huì)等待判斷當(dāng)前線程是否有前驅(qū)節(jié)點(diǎn),而是直接用當(dāng)前線程占用state資源,這是其二
小結(jié):現(xiàn)在可以理解多線程執(zhí)行一個(gè)用lock和unlock括起來(lái)的代碼發(fā)生的事情了。
多個(gè)線程到達(dá)lock()方法
如果是公平鎖,那么多個(gè)線程同時(shí)競(jìng)爭(zhēng),競(jìng)爭(zhēng)成功的就占有state資源,競(jìng)爭(zhēng)失敗的就去clh隊(duì)列里面排隊(duì),排隊(duì)過(guò)程中自旋那么一兩次進(jìn)入await等待被前驅(qū)線程喚醒。
如果是非公平鎖,那么每個(gè)線程過(guò)來(lái)直接嘗試占有資源,如果沒(méi)有成功,那么也是不排隊(duì)直接嘗試獲取資源,如果再不成功還是要進(jìn)入clh隊(duì)列排隊(duì),自旋一兩次await等待被前驅(qū)線程喚醒。
一個(gè)線程釋放資源到達(dá)unlock方法
直接通知他的后繼節(jié)點(diǎn)過(guò)來(lái)爭(zhēng)搶資源,這個(gè)過(guò)程還存在新來(lái)的線程直接獲取state的風(fēng)險(xiǎn)(非公平)。
CountDownLatch類:
先說(shuō)下該類是干啥的,調(diào)用CountDownLatch.await()方法的線程會(huì)等待構(gòu)造方法(CountDownLatch(int i))中i個(gè)線程都執(zhí)行過(guò)countDown方法后才會(huì)繼續(xù)執(zhí)行。說(shuō)白了就是調(diào)用await方法的線程請(qǐng)客,等所有線程都到齊之后這個(gè)線程才開始做飯。
下面分析下源碼實(shí)現(xiàn):
構(gòu)造方法:構(gòu)造方法實(shí)際上把state資源設(shè)置成了多份。
public CountDownLatch(int count) {
? ? ? ? if (count < 0) throw new IllegalArgumentException("count < 0");
? ? ? ? this.sync = new Sync(count);
}
Sync(int count) {
? ? ? ? ? ? setState(count);
? }
await方法:實(shí)際上是共享模式下獲取資源,當(dāng)前線程在沒(méi)有獲取到資源的情況下會(huì)進(jìn)入到資源競(jìng)爭(zhēng)隊(duì)列,共享模式下獲取資源。假如現(xiàn)在state=10共10個(gè)資源,等待隊(duì)列里有10個(gè)線程,前8個(gè)線程獲取到了10個(gè)資源,第一個(gè)和第二個(gè)線程分別占用了兩個(gè)資源,那么當(dāng)?shù)谝粋€(gè)線程釋放了2個(gè)資源后會(huì)通知整個(gè)隊(duì)列的所有標(biāo)記為shared的10個(gè)線程來(lái)競(jìng)爭(zhēng)資源,由于競(jìng)爭(zhēng)過(guò)程是公平的,所以如果此時(shí)第9個(gè)和第10個(gè)線程分別需要1個(gè)資源,那么他們兩個(gè)都會(huì)得到滿足,加入第9個(gè)需要3個(gè)資源,那么他需要等待。這就很好的解釋了CountDownLatch的await方法,由于等待的線程獲取到共享鎖之后加入到了隊(duì)列尾部,它等待的實(shí)際上是state變?yōu)?,即所有的線程都釋放,這個(gè)時(shí)候r>0執(zhí)行return;否則state!=0證明有線程在占用共享資源,那么它可能被LockSupport》park方法await等待。
public void await() throws InterruptedException {
? ? ? ? sync.acquireSharedInterruptibly(1);
}
private void doAcquireSharedInterruptibly(int arg)
? ? ? ? throws InterruptedException {
? ? ? ? final Node node = addWaiter(Node.SHARED);
? ? ? ? boolean failed = true;
? ? ? ? try {
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? final Node p = node.predecessor();
? ? ? ? ? ? ? ? if (p == head) {
? ? ? ? ? ? ? ? ? ? int r = tryAcquireShared(arg);
? ? ? ? ? ? ? ? ? ? if (r >= 0) {
? ? ? ? ? ? ? ? ? ? ? ? setHeadAndPropagate(node, r);
? ? ? ? ? ? ? ? ? ? ? ? p.next = null; // help GC
? ? ? ? ? ? ? ? ? ? ? ? failed = false;
? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ? parkAndCheckInterrupt())
? ? ? ? ? ? ? ? ? ? throw new InterruptedException();
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? if (failed)
? ? ? ? ? ? ? ? cancelAcquire(node);
? ? ? ? }
? ? }
countDown方法:可以看到該方法實(shí)際上是把資源state減一!!!!
public void countDown() {
? ? ? ? sync.releaseShared(1);
? ? }
public final boolean releaseShared(int arg) {
? ? ? ? if (tryReleaseShared(arg)) {
? ? ? ? ? ? doReleaseShared();
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return false;
? ? }
protected boolean tryReleaseShared(int releases) {
? ? ? ? ? ? // Decrement count; signal when transition to zero
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? int c = getState();
? ? ? ? ? ? ? ? if (c == 0)
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? int nextc = c-1;
? ? ? ? ? ? ? ? if (compareAndSetState(c, nextc))
? ? ? ? ? ? ? ? ? ? return nextc == 0;
? ? ? ? ? ? }
? ? ? ? }
CyclicBarrier類:
先說(shuō)下這個(gè)類是干嘛的,多個(gè)線程執(zhí)行任務(wù),每個(gè)任務(wù)內(nèi)部先調(diào)用了CyclicBarrier.await()方法后就會(huì)進(jìn)入等待,直到構(gòu)造方法中i個(gè)線程都執(zhí)行了await方法才會(huì)繼續(xù)執(zhí)行任務(wù)。相當(dāng)于4個(gè)人打麻將,所有人都到齊之后4個(gè)人才開始玩起來(lái)。
這里的源碼就不做解釋了,不是基于第二層的,而是基于Condition lock等最頂層的,簡(jiǎn)單寫個(gè)用法:
public class CyclicBarrierTest2 {
????static CyclicBarrier c = new CyclicBarrier(2, new A());
????public static void main(String[] args) {
????????new Thread(new Runnable() {
????????@Override
????????public void run() {
????????try {
????????c.await();
????????} catch (Exception e) {
????????}
????????System.out.println(1);
}
????????}).start();
????try {
????c.await();
????} catch (Exception e) {
????}
????????System.out.println(2);
????}
????static class A implements Runnable {
????????@Override
????????public void run() {
????????????System.out.println(3);
????????}
????}
}
Semphore類:
Semaphore可以控制某個(gè)資源可被同時(shí)訪問(wèn)的個(gè)數(shù),acquire()獲取一個(gè)許可,如果沒(méi)有就等待,而release()釋放一個(gè)許可。比如在Windows下可以設(shè)置共享文件的最大客戶端訪問(wèn)個(gè)數(shù)。
Semphore類本質(zhì)上把state資源分成多份,通過(guò)Shared模式獲取和釋放資源,并實(shí)現(xiàn)了公平獲取釋放和非公平獲取釋放兩種操作,我們以非公平獲取釋放為例查看其源碼:
protected int tryAcquireShared(int acquires) {
? ? ? ? ? ? return nonfairTryAcquireShared(acquires);
?}
final int nonfairTryAcquireShared(int acquires) {
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? int available = getState();
? ? ? ? ? ? ? ? int remaining = available - acquires;
? ? ? ? ? ? ? ? if (remaining < 0 ||
? ? ? ? ? ? ? ? ? ? compareAndSetState(available, remaining))
? ? ? ? ? ? ? ? ? ? return remaining;
? ? ? ? ? }
? ? }
上述代碼就是非公平鎖模式下獲取信號(hào)量資源,首先獲取state值(比如信號(hào)量總數(shù)為50),然后當(dāng)前線程需要的數(shù)acquires與可用的數(shù)做差看是否夠用,如果不夠用或者設(shè)置資源余量成功那么返回資源余量。注意設(shè)置資源余量方法是一定會(huì)執(zhí)行的。如果資源余量小于0又會(huì)進(jìn)入到AQS中的方法,涉及到排隊(duì),等待等等。這里不再贅述了!
if (tryAcquireShared(arg) < 0)
? ? ? ? ? ? doAcquireSharedInterruptibly(arg);
? ? }
protected final boolean tryReleaseShared(int releases) {
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? int current = getState();
? ? ? ? ? ? ? ? int next = current + releases;
? ? ? ? ? ? ? ? if (next < current) // overflow
? ? ? ? ? ? ? ? ? ? throw new Error("Maximum permit count exceeded");
? ? ? ? ? ? ? ? if (compareAndSetState(current, next))
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
上述是非公平模式下信號(hào)量的釋放,同樣是操作state資源,不說(shuō)了,自己看吧
公平鎖的模式就加了一個(gè)hasQueuedPredecessors判斷。。。。前文已經(jīng)解釋過(guò)了。。。。。
ConditionObject類:
Condition接口的唯一實(shí)現(xiàn)類,該類用于生產(chǎn)者消費(fèi)者模式,與Object.wait()/notify()類似但是比其更加強(qiáng)大,支持await()一段時(shí)間,還支持多個(gè)等待隊(duì)列。conditionObject最重要的兩個(gè)方法當(dāng)然是構(gòu)造方法、await()和signal()幾個(gè)方法了,下面分析其源碼:
public Condition newCondition() {
? ? ? ? return sync.newCondition();
}
上述代碼是ConditionObject的構(gòu)造方法,該Condition只能從lock中獲取,所以線程獲取Condition有兩個(gè)時(shí)機(jī),一種是調(diào)用lock()方法前,一種是調(diào)用lock()方法后。我們看下await()方法:
public final void await() throws InterruptedException {
? ? ? ? ? ? if (Thread.interrupted())
? ? ? ? ? ? ? ? throw new InterruptedException();
? ? ? ? ? ? Node node = addConditionWaiter();? //加入到當(dāng)前Condition的等待隊(duì)列
? ? ? ? ? ? int savedState = fullyRelease(node);? //完全釋放自己占有的state資源
? ? ? ? ? ? int interruptMode = 0;
? ? ? ? ? ? while (!isOnSyncQueue(node)) {? ? ? ? //如果當(dāng)前線程節(jié)點(diǎn)不在同步隊(duì)列里就一直掛起
? ? ? ? ? ? ? ? LockSupport.park(this);
? ? ? ? ? ? ? ? if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
? ? ? ? ? ? ? ? interruptMode = REINTERRUPT;
? ? ? ? ? ? if (node.nextWaiter != null) // clean up if cancelled
? ? ? ? ? ? ? ? unlinkCancelledWaiters();
? ? ? ? ? ? if (interruptMode != 0)
? ? ? ? ? ? ? ? reportInterruptAfterWait(interruptMode);
? ? }
我們看上面代碼,關(guān)鍵部分已經(jīng)做了注釋,關(guān)鍵點(diǎn)有三,其一,Condition等待隊(duì)列是啥?其二,從哪獲取資源了需要釋放?其三,為什么判斷當(dāng)前線程是否在同步隊(duì)列里?我們先看addCondtionWaiter方法:
private Node addConditionWaiter() {
? ? ? ? ? ? Node t = lastWaiter;? ? ? ? ? ? ?
????????????//這里的隊(duì)列與同步CLH隊(duì)列不是同一個(gè)隊(duì)列,通過(guò)nextWaiter而不是next指針串起來(lái)的,每個(gè)condtion都有自己的隊(duì)列
? ? ? ? ? ? if (t != null && t.waitStatus != Node.CONDITION) { //如果最后一個(gè)節(jié)點(diǎn)被取消了就清除
? ? ? ? ? ? ? ? unlinkCancelledWaiters();
? ? ? ? ? ? ? ? t = lastWaiter;
? ? ? ? ? ? }
? ? ? ? ? ? Node node = new Node(Thread.currentThread(), Node.CONDITION); //加入對(duì)了之前新建的狀態(tài)是Condition而不是0
? ? ? ? ? ? if (t == null)
? ? ? ? ? ? ? ? firstWaiter = node;
? ? ? ? ? ? else
? ? ? ? ? ? ? ? t.nextWaiter = node;? ? ? ? ?//這里體現(xiàn)出來(lái)不是CLH同步隊(duì)列,因?yàn)檎{(diào)用的是nextWaiter=node
? ? ? ? ? ? lastWaiter = node;
? ? ? ? ? ? return node;
? ? ? ? }
上述代碼,每一個(gè)Condition都有一個(gè)自己的等待隊(duì)列,使用nextWaiter指針而不是CLH隊(duì)列使用的next指針維護(hù)著隊(duì)列。如果不調(diào)用lock()的條件下直接多線程調(diào)用Condition.await方法顯而易見(jiàn)會(huì)出現(xiàn)并發(fā)問(wèn)題,所以一般await()方法都在lock.lock()鎖內(nèi)進(jìn)行。因?yàn)樵趌ock()鎖內(nèi)進(jìn)行,所以肯定存在一個(gè)CLH隊(duì)列維護(hù)著多線程的節(jié)點(diǎn),而且當(dāng)前線程一定競(jìng)爭(zhēng)到了資源才會(huì)執(zhí)行await方法,現(xiàn)在要釋放鎖讓其他線程進(jìn)來(lái),所以調(diào)用:
final int fullyRelease(Node node) {
? ? ? ? boolean failed = true;
? ? ? ? try {
? ? ? ? ? ? int savedState = getState();? ? ? //獲取當(dāng)前線程占有的資源數(shù)目
? ? ? ? ? ? if (release(savedState)) {? ? ? ? ? ?//釋放當(dāng)前占有的資源,喚醒后繼節(jié)點(diǎn)線程,返回釋放資源的數(shù)量
? ? ? ? ? ? ? ? failed = false;
? ? ? ? ? ? ? ? return savedState;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? throw new IllegalMonitorStateException();
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? if (failed)
? ? ? ? ? ? ? ? node.waitStatus = Node.CANCELLED;
? ? ? ? }
? ? }
上述代碼表示當(dāng)前線程在真正等待之前首先釋放了資源并喚醒了CLH隊(duì)列中的后繼線程。釋放了資源之后現(xiàn)在該考慮掛起了。
final boolean isOnSyncQueue(Node node) {? ? //while循環(huán)執(zhí)行該代碼
? ? ? ? if (node.waitStatus == Node.CONDITION || node.prev == null)? //如果當(dāng)前線程節(jié)點(diǎn)狀態(tài)是Condition或者不在CLH隊(duì)列中返回false
? ? ? ? ? ? return false;
? ? ? ? if (node.next != null)? ? ? ? ? ? ? ? ? ? ? //如果當(dāng)前線程節(jié)點(diǎn)有后繼節(jié)點(diǎn)證明在CLH隊(duì)列中,返回True
? ? ? ? ? ? return true;
? ? ? ? return findNodeFromTail(node);? ?//該方法同樣是判斷是否在同步隊(duì)列中
? ? }
private boolean findNodeFromTail(Node node) {
????Node t = tail;
????for (;;) {
????????if (t == node)
????????????return true;
????????if (t == null)
????????????return false;
????????????t = t.prev;
????}
}
當(dāng)前線程不在CLH隊(duì)列中就會(huì)把自己掛起,我們看下signal方法來(lái)解釋下為什么不在CLH隊(duì)列中就執(zhí)行掛起
public final void signal() {
? ? ? ? ? ? if (!isHeldExclusively())
? ? ? ? ? ? ? ? throw new IllegalMonitorStateException();
? ? ? ? ? ? Node first = firstWaiter;
? ? ? ? ? ? if (first != null)? ? ? ? ? ? ? ? //如果Condition的等待隊(duì)列里面有等待線程就執(zhí)行通知,否則啥也不做。
? ? ? ? ? ? ? ? doSignal(first);
? ? ? ? }
private void doSignal(Node first) {
? ? ? ? ? ? do {? ? ? ? ? ? //do while循環(huán),關(guān)鍵方法在transferForSignal方法
? ? ? ? ? ? ? ? if ( (firstWaiter = first.nextWaiter) == null)
? ? ? ? ? ? ? ? ? ? lastWaiter = null;
? ? ? ? ? ? ? ? first.nextWaiter = null;
? ? ? ? ? ? } while (!transferForSignal(first) &&(first = firstWaiter) != null);
? ? ? ? }
如果Condition等待隊(duì)列的頭結(jié)點(diǎn)沒(méi)有把狀態(tài)變換成SIGNAL就一直執(zhí)行do循環(huán)清空等待隊(duì)列,我們看下這個(gè)方法做了什么?
final boolean transferForSignal(Node node) {
? ? ? ? if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))? ??
? ? ? ? ? ? return false;
? ? ? ? //把當(dāng)前節(jié)點(diǎn)放入CLH隊(duì)列,這也解釋了之前為什么不在CLH隊(duì)列就一直掛起!
? ? ? ? Node p = enq(node);
? ? ? ? int ws = p.waitStatus;
? ? ? ? if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) //如果當(dāng)前節(jié)點(diǎn)被取消或者設(shè)置SIGNAL狀態(tài)失敗那么喚醒該線程。
? ? ? ? ? ? LockSupport.unpark(node.thread);
? ? ? ? return true;
? ? }
上面就是喚醒Condition線程的關(guān)鍵代碼,你可能會(huì)有疑問(wèn),從哪喚醒了等待隊(duì)列中的線程呢?哈哈,關(guān)鍵就在于enq(node)就是把當(dāng)前線程加入到了CLH隊(duì)列中并把等待節(jié)點(diǎn)的狀態(tài)設(shè)置成了SIGNAL,還記得之前CLH隊(duì)列中的設(shè)置SIGNAL嗎?(告訴前面線程如果排到號(hào)了通知我),沒(méi)錯(cuò),實(shí)際情況就是signal方法調(diào)用之后當(dāng)前線程需要重新進(jìn)入到CLH隊(duì)列競(jìng)爭(zhēng)鎖,而且是排在隊(duì)尾哦。。。
總結(jié):
最高層里面大多數(shù)的類都依賴AQS框架的state和acquire/release方法。這就是AQS框架的精妙所在,模板方法模式,一勞永逸。
本文很長(zhǎng),后續(xù)還會(huì)對(duì)阻塞隊(duì)列、并發(fā)容器、執(zhí)行器做源碼分析。