您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“ReentrantLock源碼分析”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
從類(lèi)圖我們可以直觀地了解到,ReentrantLock最終還是使用AQS來(lái)實(shí)現(xiàn)地,并且根據(jù)參數(shù)來(lái)決定其內(nèi)部是一個(gè)公平????還是非公平鎖????,默認(rèn)是非公平鎖????。
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
其中Sync類(lèi)直接繼承自AQS,它的子類(lèi)NonfairSync和FairSync分別實(shí)現(xiàn)了獲取鎖的非公平與公平策略。
如果讀者對(duì)AQS還不了解的話(huà),可以去看看我的這篇文章:抽象同步隊(duì)列AQS——AbstractQueuedSynchronizer鎖詳解
在這里,AQS的state狀態(tài)值表示線(xiàn)程獲取該鎖的可重入次數(shù),在默認(rèn)情況下,state的值為0表示當(dāng)前鎖沒(méi)有被任何線(xiàn)程持有。當(dāng)一個(gè)線(xiàn)程第一次獲取該鎖時(shí),會(huì)嘗試使用CAS設(shè)置state的值為1,
如果CAS成功則當(dāng)前線(xiàn)程獲取了該鎖,然后記錄該鎖的持有者為當(dāng)前線(xiàn)程。在該線(xiàn)程沒(méi)用釋放鎖的情況下第二次獲取該鎖后,狀態(tài)值被設(shè)置為2,這就是可重入次數(shù)。
在該線(xiàn)程釋放鎖時(shí),會(huì)嘗試使用CAS讓狀態(tài)值減1,如果減1后狀態(tài)值為0,則當(dāng)前線(xiàn)程釋放該鎖。
lock()獲取鎖,其實(shí)就是把state從0變成n(重入鎖可以累加)。實(shí)際調(diào)用的是sync的lock方法,分公平和非公平。
public void lock() { sync.lock(); }
在如上代碼中,ReentrantLock的lock()委托給sync類(lèi),根據(jù)創(chuàng)建的ReentrantLock構(gòu)造函數(shù)選擇sync的實(shí)現(xiàn)是NonfairSync還是FairSync,先看看sync的子類(lèi)NonfairSync(非公平鎖????)的情況
final void lock() { if (compareAndSetState(0, 1))//CAS設(shè)置狀態(tài)值為1 setExclusiveOwnerThread(Thread.currentThread());//設(shè)置該鎖的持有者為當(dāng)前線(xiàn)程 else //CAS失敗的話(huà) acquire(1);//調(diào)用AQS的acquire方法,傳遞參數(shù)為1 }
下面是AQS的acquire的核心源碼
public final void acquire(int arg) { if (!tryAcquire(arg) &&//調(diào)用ReentantLock重寫(xiě)tryAcquire方法 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//tryAcquire返回false會(huì)把當(dāng)前線(xiàn)程放入AQS阻塞隊(duì)列 selfInterrupt(); }
之前說(shuō)過(guò),AQS并沒(méi)有提供可用的tryAcquire方法,tryAcquire方法需要子類(lèi)自己定制化,所以這里代碼會(huì)調(diào)用ReentantLock重寫(xiě)的tryAcquire方法。我們看下非公平鎖????的實(shí)現(xiàn)
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//當(dāng)前AQS狀態(tài)為0,acquires參數(shù)傳遞默認(rèn)為1,因?yàn)橹癈AS失敗,再次獲取鎖 if (compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài)值為1 setExclusiveOwnerThread(current);//設(shè)置該鎖的持有者為當(dāng)前的線(xiàn)程 return true; } } else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線(xiàn)程是該鎖的持有者 int nextc = c + acquires;//獲取過(guò)了就累加,因?yàn)榭芍厝? if (nextc < 0) // overflow//說(shuō)明可重入次數(shù)溢出了 throw new Error("Maximum lock count exceeded"); setState(nextc);//重新設(shè)置鎖的狀態(tài) return true; } return false;//如果當(dāng)前線(xiàn)程不是該鎖的持有者,則返回false,然后會(huì)放入AQS阻塞隊(duì)列 }
結(jié)束完非公平鎖????的實(shí)現(xiàn)代碼,回過(guò)頭來(lái)看看非公平在這里是怎么體現(xiàn)的。首先非公平是說(shuō)先嘗試獲取鎖的線(xiàn)程并不一定比后嘗試獲取鎖的線(xiàn)程優(yōu)先獲取鎖????。
而是使用了搶奪策略。那么下面我們看看公平鎖????是怎么實(shí)現(xiàn)公平的。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//當(dāng)前AQS狀態(tài)為0 if (!hasQueuedPredecessors() &&//公平性策略,判斷隊(duì)列還有沒(méi)有其它node,要保證公平 compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài) setExclusiveOwnerThread(current);//設(shè)置獲取鎖的線(xiàn)程 return true; } } else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線(xiàn)程是該鎖的持有者 int nextc = c + acquires;//重入次數(shù)+1 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc);//重新設(shè)置鎖的狀態(tài) return true; } return false; } }
如上代碼所示,公平的tryAcquire策略與非公平的類(lèi)似,不同之處在于,代碼在設(shè)置CAS操作之前添加了hasQueuedPredecessors()方法,該方法是實(shí)現(xiàn)公平性的核心代碼。代碼如下
public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
該方法與lock()方法類(lèi)似,不同在于對(duì)中斷進(jìn)行響應(yīng),如果當(dāng)前線(xiàn)程在調(diào)用該方法時(shí),其它線(xiàn)程調(diào)用了當(dāng)前線(xiàn)程的interrupt()方法,則該線(xiàn)程拋出異常而返回
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted())//如果當(dāng)前線(xiàn)程被中斷,則直接拋出異常 throw new InterruptedException(); if (!tryAcquire(arg))//嘗試獲取資源 doAcquireInterruptibly(arg);//調(diào)用AQS可被中斷的方法 }
嘗試獲取鎖,如果當(dāng)前鎖沒(méi)用被其它線(xiàn)程持有,則當(dāng)前線(xiàn)程獲取該鎖并返回true,否則返回false。注意,該方法不會(huì)引起當(dāng)前線(xiàn)程阻塞
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
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; }
如上代碼與非公平鎖的tryAcquire()方法代碼類(lèi)似,所以tryLock()使用的是非公平策略。
嘗試獲取鎖,與tryLock()的不同之處在于,它設(shè)置了超時(shí)時(shí)間,如果超時(shí)時(shí)間到了,沒(méi)用獲取到鎖,則返回false,以下是相關(guān)代碼
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout));//調(diào)用AQS的tryAcquireNanos方法 }
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
嘗試獲取鎖,如果當(dāng)前線(xiàn)程持有鎖,則調(diào)用該方法會(huì)讓該線(xiàn)程持有的AQS狀態(tài)值減1,如果減1后當(dāng)前狀態(tài)值為0,則當(dāng)前線(xiàn)程會(huì)釋放該鎖,否則僅僅減1而已。
如果當(dāng)前線(xiàn)程沒(méi)用持有該鎖而調(diào)用了該方法則會(huì)拋出異常,代碼如下:
public void unlock() { sync.release(1); }
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
protected final boolean tryRelease(int releases) { int c = getState() - releases;//AQS狀態(tài)值減1 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) {//如果當(dāng)前可重入次數(shù)為0,則清空鎖持有線(xiàn)程 free = true; setExclusiveOwnerThread(null); } setState(c);//設(shè)置可重入次數(shù)為原始值減1 return free; }
下面使用ReentrantLock來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線(xiàn)程安全的list集合
public class ReentrantLockList { //線(xiàn)程不安全的list private ArrayList<String>arrayList=new ArrayList<>(); //獨(dú)占鎖 private volatile ReentrantLock lock=new ReentrantLock(); //添加元素 public void add(String e){ lock.lock(); try { arrayList.add(e); }finally { lock.unlock(); } } //刪除元素 public void remove(String e){ lock.lock(); try { arrayList.remove(e); }finally { lock.unlock(); } } //獲取數(shù)據(jù) public String get(int index){ lock.lock(); try { return arrayList.get(index); }finally { lock.unlock(); } } }
如上代碼在操作arrayList元素前進(jìn)行加鎖保證同一時(shí)間只有一個(gè)線(xiàn)程可用對(duì)arrayList數(shù)組進(jìn)行修改,但是也只能一個(gè)線(xiàn)程對(duì)arrayList進(jìn)行訪(fǎng)問(wèn)。
如圖,假如線(xiàn)程Thread-1,Thread-2,Thread-3同時(shí)嘗試獲取獨(dú)占鎖ReentrantLock,加上Thread-1獲取到了????,則Thread-2和Thread-3就會(huì)被轉(zhuǎn)換為Node節(jié)點(diǎn)并放入ReentrantLock對(duì)應(yīng)的AQS阻塞隊(duì)列,而后阻塞掛起。
如圖,假設(shè)Thread-1獲取鎖后調(diào)用了對(duì)應(yīng)的鎖創(chuàng)建的條件變量1,那么Thread-1就會(huì)釋放獲取到的????,然后當(dāng)前線(xiàn)程就會(huì)被轉(zhuǎn)換為Node節(jié)點(diǎn)插入條件變量1的條件隊(duì)列。由于Thread-1釋放了????,所以阻塞到AQS隊(duì)列里面的
Thread-2和Thread-3就會(huì)有機(jī)會(huì)獲取到該鎖,假如使用的是公平性策略,那么者時(shí)候Thread-2會(huì)獲取到鎖,從而從AQS隊(duì)列里面移除Thread-2對(duì)應(yīng)的Node節(jié)點(diǎn)。
“ReentrantLock源碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。