Java并發(fā)編程之ReentrantLock解析
ReentrantLock
前篇寫(xiě)了JUC的基礎(chǔ)AQS,其中介紹了它提供的很多模板方法,但是在實(shí)際編程中我們不會(huì)直接使用它,而是會(huì)使用它的各種實(shí)現(xiàn)。
本文將介紹在實(shí)際使用中出現(xiàn)頻率很高的一種并發(fā)鎖——ReentrantLock。
從名字上來(lái)看,ReentrantLock具有兩個(gè)特性,一個(gè)是可重入,一個(gè)是鎖。
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer { ... } static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ 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; } } public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } public void lock() { sync.lock(); } public void unlock() { sync.release(1); } }
ReentrantLock內(nèi)容定義了一個(gè)抽象類(lèi)Sync,繼承自AQS,而不是自己去繼承AQS,所有對(duì)ReentrantLock的操作都會(huì)轉(zhuǎn)化為對(duì)Sync的操作。同時(shí)又定義了Sync的兩個(gè)子類(lèi)FairSync和NonfairSync,分別用于實(shí)現(xiàn)公平鎖和非公平鎖。除非你在生成ReentrantLock時(shí)顯示的指明需要公平鎖,否則默認(rèn)采用非公平鎖。
可重入
先來(lái)看下可重入如何實(shí)現(xiàn),以默認(rèn)的非公平鎖舉例??芍厝?,意味著線程在獲取鎖之后,還可以再次獲取鎖,同樣,獲取了多少次,就要釋放多少次,否則資源釋放不對(duì),別的線程將永遠(yuǎn)無(wú)法獲得鎖。
final void lock() { //1、CAS將state置為1 if (compareAndSetState(0, 1)) //2、設(shè)置自己為獨(dú)占線程 setExclusiveOwnerThread(Thread.currentThread()); else //3、否則嘗試獲取資源 acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //1、如果state為0,說(shuō)明當(dāng)前鎖沒(méi)有被占用 if (c == 0) { //2、CAS嘗試將state設(shè)為1 if (compareAndSetState(0, acquires)) { //3、獲取鎖成功,設(shè)置自己為獨(dú)占線程 setExclusiveOwnerThread(current); return true; } } //4、如果持有當(dāng)前鎖的線程就是自己 else if (current == getExclusiveOwnerThread()) { //5、那么將state增加acquires int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //6、更新state,因?yàn)殒i已經(jīng)被自己持有了,所以這里不需要CAS設(shè)置 setState(nextc); return true; } return false; } //AQS public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
獲取鎖的過(guò)程中,4、5、6三步即實(shí)現(xiàn)了可重入獲取。再看下釋放。
protected final boolean tryRelease(int releases) { //1、每次釋放,將state減去釋放數(shù) int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //2、如果state為0,說(shuō)明所有資源都已經(jīng)釋放 if (c == 0) { free = true; //3、將獨(dú)占線程置空 setExclusiveOwnerThread(null); } //4、更新state setState(c); return free; }
同樣,釋放時(shí)需要將獲取的資源依次扣除,什么時(shí)候state減為0了,才算該線程持有的所有資源都釋放掉了。
公平鎖實(shí)現(xiàn)可重入同理,不再贅述。
公平與非公平
那么公平鎖與非公平鎖又有什么區(qū)別呢?
非公平鎖的獲取上面已經(jīng)分析了,來(lái)看下公平鎖的獲取,看下有什么不同。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //1、等待隊(duì)列中是否已經(jīng)有節(jié)點(diǎn)在等待獲取鎖了 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; } public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; //2、等待隊(duì)列以后已經(jīng)初始化,并且有別的線程正在入隊(duì)(enq)或者已經(jīng)入隊(duì) return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
公平和非公平的區(qū)別就在于,嘗試獲取鎖前要看下是否有線程已經(jīng)在自己之前就開(kāi)始等待了,如果沒(méi)有才去競(jìng)爭(zhēng)。通過(guò)這種方式保證公平,即先等先得。
公平鎖和非公平鎖哪種性能更好呢,公平鎖雖然能保證等待最久的線程可以先獲得鎖,但是這同時(shí)也以為著每次都會(huì)是不同的線程獲取鎖,每次都要進(jìn)行線程切換?!禞ava并發(fā)編程的藝術(shù)》進(jìn)行了測(cè)試,表明非公平鎖雖然可能造成某些線程一直獲取不到鎖,但是可以提高整體的吞吐量,所以ReentrantLock將其作為了默認(rèn)實(shí)現(xiàn)。如果是需要保證這種先等先得的特性,也可以使用公平鎖。
與synchronized對(duì)比
ReentrantLock在加鎖和內(nèi)存語(yǔ)義上提供了與synchronized相同的功能,此外還提供了定時(shí)、可中斷、公平性等特性。
JDK5時(shí),ReentrantLock的性能要遠(yuǎn)優(yōu)于synchronized,但是JDK6引入了synchronized的鎖優(yōu)化,兩者之間的差別并沒(méi)有那么大了。
除非需要一些高級(jí)功能,如可定時(shí)、可輪詢(xún)、可中斷、公平性等,才使用ReentrantLock,否則應(yīng)該優(yōu)先使用synchronized,畢竟大部分人都用習(xí)慣了,而且使用簡(jiǎn)單。
到此這篇關(guān)于Java并發(fā)編程之ReentrantLock解析的文章就介紹到這了,更多相關(guān)Java的ReentrantLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中@Autowired 失效及@Autowired 注入為null的解決
在SpringBoot項(xiàng)目中,@Autowired注解用于自動(dòng)注入依賴(lài),但有時(shí)會(huì)注入失敗導(dǎo)致空指針異常,下面就來(lái)介紹一下如何解決,感興趣的可以了解一下2024-09-09解決Spring boot 嵌入的tomcat不啟動(dòng)問(wèn)題
這篇文章主要介紹了解決Spring boot 嵌入的tomcat不啟動(dòng)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10java自定義注解實(shí)現(xiàn)前后臺(tái)參數(shù)校驗(yàn)的實(shí)例
下面小編就為大家?guī)?lái)一篇java自定義注解實(shí)現(xiàn)前后臺(tái)參數(shù)校驗(yàn)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11java 中枚舉類(lèi)enum的values()方法的詳解
這篇文章主要介紹了java 中枚舉類(lèi)enum的values()方法的詳解的相關(guān)資料,希望通過(guò)本文大家能夠掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09mybatis分頁(yè)絕對(duì)路徑寫(xiě)法過(guò)程詳解
這篇文章主要介紹了mybatis分頁(yè)絕對(duì)路徑寫(xiě)法過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之養(yǎng)老院管理系統(tǒng)的實(shí)現(xiàn)
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+JSP+Easyui+maven+mysql實(shí)現(xiàn)一個(gè)養(yǎng)老院管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2022-03-03利用java反射機(jī)制實(shí)現(xiàn)自動(dòng)調(diào)用類(lèi)的簡(jiǎn)單方法
下面小編就為大家?guī)?lái)一篇利用java反射機(jī)制實(shí)現(xiàn)自動(dòng)調(diào)用類(lèi)的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08