本篇內(nèi)容介紹了“ReentrantLock源碼分析”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比郎溪網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式郎溪網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋郎溪地區(qū)。費(fèi)用合理售后完善,10多年實體公司更值得信賴。
從類圖我們可以直觀地了解到,ReentrantLock最終還是使用AQS來實現(xiàn)地,并且根據(jù)參數(shù)來決定其內(nèi)部是一個公平????還是非公平鎖????,默認(rèn)是非公平鎖????。
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
其中Sync類直接繼承自AQS,它的子類NonfairSync和FairSync分別實現(xiàn)了獲取鎖的非公平與公平策略。
如果讀者對AQS還不了解的話,可以去看看我的這篇文章:抽象同步隊列AQS——AbstractQueuedSynchronizer鎖詳解
在這里,AQS的state狀態(tài)值表示線程獲取該鎖的可重入次數(shù),在默認(rèn)情況下,state的值為0表示當(dāng)前鎖沒有被任何線程持有。當(dāng)一個線程第一次獲取該鎖時,會嘗試使用CAS設(shè)置state的值為1,
如果CAS成功則當(dāng)前線程獲取了該鎖,然后記錄該鎖的持有者為當(dāng)前線程。在該線程沒用釋放鎖的情況下第二次獲取該鎖后,狀態(tài)值被設(shè)置為2,這就是可重入次數(shù)。
在該線程釋放鎖時,會嘗試使用CAS讓狀態(tài)值減1,如果減1后狀態(tài)值為0,則當(dāng)前線程釋放該鎖。
lock()獲取鎖,其實就是把state從0變成n(重入鎖可以累加)。實際調(diào)用的是sync的lock方法,分公平和非公平。
public void lock() { sync.lock(); }
在如上代碼中,ReentrantLock的lock()委托給sync類,根據(jù)創(chuàng)建的ReentrantLock構(gòu)造函數(shù)選擇sync的實現(xiàn)是NonfairSync還是FairSync,先看看sync的子類NonfairSync(非公平鎖????)的情況
final void lock() { if (compareAndSetState(0, 1))//CAS設(shè)置狀態(tài)值為1 setExclusiveOwnerThread(Thread.currentThread());//設(shè)置該鎖的持有者為當(dāng)前線程 else //CAS失敗的話 acquire(1);//調(diào)用AQS的acquire方法,傳遞參數(shù)為1 }
下面是AQS的acquire的核心源碼
public final void acquire(int arg) { if (!tryAcquire(arg) &&//調(diào)用ReentantLock重寫tryAcquire方法 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//tryAcquire返回false會把當(dāng)前線程放入AQS阻塞隊列 selfInterrupt(); }
之前說過,AQS并沒有提供可用的tryAcquire方法,tryAcquire方法需要子類自己定制化,所以這里代碼會調(diào)用ReentantLock重寫的tryAcquire方法。我們看下非公平鎖????的實現(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,因為之前CAS失敗,再次獲取鎖 if (compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài)值為1 setExclusiveOwnerThread(current);//設(shè)置該鎖的持有者為當(dāng)前的線程 return true; } } else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線程是該鎖的持有者 int nextc = c + acquires;//獲取過了就累加,因為可重入 if (nextc < 0) // overflow//說明可重入次數(shù)溢出了 throw new Error("Maximum lock count exceeded"); setState(nextc);//重新設(shè)置鎖的狀態(tài) return true; } return false;//如果當(dāng)前線程不是該鎖的持有者,則返回false,然后會放入AQS阻塞隊列 }
結(jié)束完非公平鎖????的實現(xiàn)代碼,回過頭來看看非公平在這里是怎么體現(xiàn)的。首先非公平是說先嘗試獲取鎖的線程并不一定比后嘗試獲取鎖的線程優(yōu)先獲取鎖????。
而是使用了搶奪策略。那么下面我們看看公平鎖????是怎么實現(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() &&//公平性策略,判斷隊列還有沒有其它node,要保證公平 compareAndSetState(0, acquires)) {//CAS設(shè)置狀態(tài) setExclusiveOwnerThread(current);//設(shè)置獲取鎖的線程 return true; } } else if (current == getExclusiveOwnerThread()) {//如果當(dāng)前線程是該鎖的持有者 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策略與非公平的類似,不同之處在于,代碼在設(shè)置CAS操作之前添加了hasQueuedPredecessors()方法,該方法是實現(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()方法類似,不同在于對中斷進(jìn)行響應(yīng),如果當(dāng)前線程在調(diào)用該方法時,其它線程調(diào)用了當(dāng)前線程的interrupt()方法,則該線程拋出異常而返回
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted())//如果當(dāng)前線程被中斷,則直接拋出異常 throw new InterruptedException(); if (!tryAcquire(arg))//嘗試獲取資源 doAcquireInterruptibly(arg);//調(diào)用AQS可被中斷的方法 }
嘗試獲取鎖,如果當(dāng)前鎖沒用被其它線程持有,則當(dāng)前線程獲取該鎖并返回true,否則返回false。注意,該方法不會引起當(dāng)前線程阻塞
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()方法代碼類似,所以tryLock()使用的是非公平策略。
嘗試獲取鎖,與tryLock()的不同之處在于,它設(shè)置了超時時間,如果超時時間到了,沒用獲取到鎖,則返回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)前線程持有鎖,則調(diào)用該方法會讓該線程持有的AQS狀態(tài)值減1,如果減1后當(dāng)前狀態(tài)值為0,則當(dāng)前線程會釋放該鎖,否則僅僅減1而已。
如果當(dāng)前線程沒用持有該鎖而調(diào)用了該方法則會拋出異常,代碼如下:
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,則清空鎖持有線程 free = true; setExclusiveOwnerThread(null); } setState(c);//設(shè)置可重入次數(shù)為原始值減1 return free; }
下面使用ReentrantLock來實現(xiàn)一個簡單的線程安全的list集合
public class ReentrantLockList { //線程不安全的list private ArrayListarrayList=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)行加鎖保證同一時間只有一個線程可用對arrayList數(shù)組進(jìn)行修改,但是也只能一個線程對arrayList進(jìn)行訪問。
如圖,假如線程Thread-1,Thread-2,Thread-3同時嘗試獲取獨(dú)占鎖ReentrantLock,加上Thread-1獲取到了????,則Thread-2和Thread-3就會被轉(zhuǎn)換為Node節(jié)點(diǎn)并放入ReentrantLock對應(yīng)的AQS阻塞隊列,而后阻塞掛起。
如圖,假設(shè)Thread-1獲取鎖后調(diào)用了對應(yīng)的鎖創(chuàng)建的條件變量1,那么Thread-1就會釋放獲取到的????,然后當(dāng)前線程就會被轉(zhuǎn)換為Node節(jié)點(diǎn)插入條件變量1的條件隊列。由于Thread-1釋放了????,所以阻塞到AQS隊列里面的
Thread-2和Thread-3就會有機(jī)會獲取到該鎖,假如使用的是公平性策略,那么者時候Thread-2會獲取到鎖,從而從AQS隊列里面移除Thread-2對應(yīng)的Node節(jié)點(diǎn)。
“ReentrantLock源碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!