原文鏈接:深入剖析基于并發(fā)AQS的(獨占鎖)重入鎖(ReetrantLock)及其Condition實現(xiàn)原理 - CSDN博客
Lock接口
前面我們詳談過解決多線程同步問題的關(guān)鍵字synchronized冲九,synchronized屬于隱式鎖荧关,即鎖的持有與釋放都是隱式的,我們無需干預格仲,而本篇我們要講解的是顯式鎖搁廓,即鎖的持有和釋放都必須由我們手動編寫引颈。在Java 1.5中直晨,官方在concurrent并發(fā)包中加入了Lock接口阱持,該接口中提供了lock()方法和unLock()方法對顯式加鎖和顯式釋放鎖操作進行支持树碱,簡單了解一下代碼編寫北秽,如下:
Lock lock = new ReentrantLock();
lock.lock();
try{
? ? //臨界區(qū)......
}finally{
? ? lock.unlock();
}
正如代碼所顯示(ReentrantLock是Lock的實現(xiàn)類,稍后分析)矛辕,當前線程使用lock()方法與unlock()對臨界區(qū)進行包圍收苏,其他線程由于無法持有鎖將無法進入臨界區(qū)直到當前線程釋放鎖苗桂,注意unlock()操作必須在finally代碼塊中逼泣,這樣可以確保即使臨界區(qū)執(zhí)行拋出異常趴泌,線程最終也能正常釋放鎖,Lock接口還提供了鎖以下相關(guān)方法:
public interfaceLock{
????//加鎖
????void lock();
? ? //解鎖
? ? void unlock();
? ? //可中斷獲取鎖拉庶,與lock()不同之處在于可響應中斷操作,即在獲
? ? //取鎖的過程中可中斷秃励,注意synchronized在獲取鎖時是不可中斷的
? ? void lockInterruptibly() throws InterruptedException;
? ? //嘗試非阻塞獲取鎖氏仗,調(diào)用該方法后立即返回結(jié)果,如果能夠獲取則返回true夺鲜,否則返回false
? ? boolean tryLock();
? ? //根據(jù)傳入的時間段獲取鎖皆尔,在指定時間內(nèi)沒有獲取鎖則返回false,如果在指定時間內(nèi)當前線程未被中并斷獲取到鎖則返回true
? ? boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
? ? //獲取等待通知組件币励,該組件與當前鎖綁定慷蠕,當前線程只有獲得了鎖
? ? //才能調(diào)用該組件的wait()方法,而調(diào)用后食呻,當前線程將釋放鎖流炕。
? ? Condition newCondition();
可見Lock對象鎖還提供了synchronized所不具備的其他同步特性,如可中斷鎖的獲取(synchronized在等待獲取鎖時是不可中的)仅胞,超時中斷鎖的獲取每辟,等待喚醒機制的多條件變量Condition等,這也使得Lock鎖在使用上具有更大的靈活性干旧。下面進一步分析Lock的實現(xiàn)類重入鎖ReetrantLock渠欺。
重入鎖ReetrantLock
重入鎖ReetrantLock,JDK 1.5新增的類椎眯,實現(xiàn)了Lock接口挠将,作用與synchronized關(guān)鍵字相當胳岂,但比synchronized更加靈活。ReetrantLock本身也是一種支持重進入的鎖舔稀,即該鎖可以支持一個線程對資源重復加鎖旦万,同時也支持公平鎖與非公平鎖。所謂的公平與非公平指的是在請求先后順序上镶蹋,先對鎖進行請求的就一定先獲取到鎖成艘,那么這就是公平鎖,反之贺归,如果對于鎖的獲取并沒有時間上的先后順序淆两,如后請求的線程可能先獲取到鎖,這就是非公平鎖拂酣,一般而言非秋冰,非公平鎖機制的效率往往會勝過公平鎖的機制,但在某些場景下婶熬,可能更注重時間先后順序剑勾,那么公平鎖自然是很好的選擇。需要注意的是ReetrantLock支持對同一線程重加鎖赵颅,但是加鎖多少次虽另,就必須解鎖多少次,這樣才可以成功釋放鎖饺谬。下面看看ReetrantLock的簡單使用案例:
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLock implements Runnable{?
? ? public static ReentrantLock lock=new ReentrantLock();
? ? public static int i=0;
? ? @Override
? ? public void run() {
? ? ? ? for(int j=0;j<10000000;j++){
? ? ? ? ? ? lock.lock();
? ? ? ? ? ? //支持重入鎖
? ? ? ? ? ? lock.lock();
? ? ? ? ? ? try{
? ? ? ? ? ? ? ? i++;
? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? //執(zhí)行兩次解鎖
? ? ? ? ? ? ? ? lock.unlock();
? ? ? ? ? ? ? ? lock.unlock();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public static void main(String[] args) throws InterruptedException {
? ? ? ? ReenterLock tl=new ReenterLock();
? ? ? ? Thread t1=new Thread(tl);
? ? ? ? Thread t2=new Thread(tl);
? ? ? ? t1.start();
????????t2.start();
? ? ? ? t1.join();
????????t2.join();
? ? ? ? //輸出結(jié)果:20000000
? ? ? ? System.out.println(i);
? ? }
}
代碼非常簡單捂刺,我們使用兩個線程同時操作臨界資源i,執(zhí)行自增操作募寨,使用ReenterLock進行加鎖族展,解決線程安全問題,這里進行了兩次重復加鎖拔鹰,由于ReenterLock支持重入仪缸,因此這樣是沒有問題的,需要注意的是在finally代碼塊中列肢,需執(zhí)行兩次解鎖操作才能真正成功地讓當前執(zhí)行線程釋放鎖恰画,從這里看ReenterLock的用法還是非常簡單的,除了實現(xiàn)Lock接口的方法例书,ReenterLock其他方法說明如下:
//查詢當前線程保持此鎖的次數(shù)锣尉。
int getHoldCount();
//返回目前擁有此鎖的線程,如果此鎖不被任何線程擁有决采,則返回 null自沧。
protected Thread getOwner();
//返回一個 collection,它包含可能正等待獲取此鎖的線程,其內(nèi)部維持一個隊列拇厢,這點稍后會分析爱谁。?
protected Collection getQueuedThreads();
//返回正等待獲取此鎖的線程估計數(shù)。?
int getQueueLength();
// 返回一個 collection孝偎,它包含可能正在等待與此鎖相關(guān)給定條件的那些線程访敌。
protected Collection getWaitingThreads(Condition condition);?
//返回等待與此鎖相關(guān)的給定條件的線程估計數(shù)。?
int getWaitQueueLength(Condition condition);
// 查詢給定線程是否正在等待獲取此鎖衣盾。?
boolean hasQueuedThread(Thread thread);?
//查詢是否有些線程正在等待獲取此鎖寺旺。?
boolean hasQueuedThreads();
//查詢是否有些線程正在等待與此鎖有關(guān)的給定條件。?
boolean hasWaiters(Condition condition);?
//如果此鎖的公平設(shè)置為 true势决,則返回 true阻塑。?
boolean isFair();?
//查詢當前線程是否保持此鎖。?
boolean isHeldByCurrentThread();?
//查詢此鎖是否由任意線程保持果复。?
boolean isLocked();
由于ReetrantLock鎖在使用上還是比較簡單的陈莽,也就暫且打住,下面著重分析一下ReetrantLock的內(nèi)部實現(xiàn)原理虽抄,這才是本篇博文的重點走搁。實際上ReetrantLock是基于AQS并發(fā)框架實現(xiàn)的,我們先深入了解AQS,然后一步步揭開ReetrantLock的內(nèi)部實現(xiàn)原理迈窟。
并發(fā)基礎(chǔ)組件AQS與ReetrantLock
AQS工作原理概要
AbstractQueuedSynchronizer又稱為隊列同步器(后面簡稱AQS)私植,它是用來構(gòu)建鎖或其他同步組件的基礎(chǔ)框架,內(nèi)部通過一個int類型的成員變量state來控制同步狀態(tài),當state=0時菠隆,則說明沒有任何線程占有共享資源的鎖兵琳,當state=1時,則說明有線程目前正在使用共享變量骇径,其他線程必須加入同步隊列進行等待,AQS內(nèi)部通過內(nèi)部類Node構(gòu)成FIFO的同步隊列來完成線程獲取鎖的排隊工作者春,同時利用內(nèi)部類ConditionObject構(gòu)建等待隊列破衔,當Condition調(diào)用wait()方法后,線程將會加入等待隊列中钱烟,而當Condition調(diào)用signal()方法后晰筛,線程將從等待隊列轉(zhuǎn)移動同步隊列中進行鎖競爭。注意這里涉及到兩種隊列拴袭,一種的同步隊列读第,當線程請求鎖而等待的后將加入同步隊列等待,而另一種則是等待隊列(可有多個)拥刻,通過Condition調(diào)用await()方法釋放鎖后怜瞒,將加入等待隊列。關(guān)于Condition的等待隊列我們后面再分析,這里我們先來看看AQS中的同步隊列模型吴汪,如下:
/**
* AQS抽象類
*/
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer{
//指向同步隊列隊頭
private transient volatile Node head;
//指向同步的隊尾
private transient volatile Node tail;
//同步狀態(tài)惠窄,0代表鎖未被占用,1代表鎖已被占用
private volatile int state;
//省略其他代碼......
}
head和tail分別是AQS中的變量漾橙,其中head指向同步隊列的頭部杆融,注意head為空結(jié)點,不存儲信息霜运。而tail則是同步隊列的隊尾脾歇,同步隊列采用的是雙向鏈表的結(jié)構(gòu)這樣可方便隊列進行結(jié)點增刪操作。state變量則是代表同步狀態(tài)淘捡,執(zhí)行當線程調(diào)用lock方法進行加鎖后藕各,如果此時state的值為0,則說明當前線程可以獲取到鎖(在本篇文章中案淋,鎖和同步狀態(tài)代表同一個意思)座韵,同時將state設(shè)置為1,表示獲取成功踢京。如果state已為1誉碴,也就是當前鎖已被其他線程持有,那么當前執(zhí)行線程將被封裝為Node結(jié)點加入同步隊列等待瓣距。其中Node結(jié)點是對每一個訪問同步代碼的線程的封裝黔帕,從圖中的Node的數(shù)據(jù)結(jié)構(gòu)也可看出,其包含了需要同步的線程本身以及線程的狀態(tài)蹈丸,如是否被阻塞成黄,是否等待喚醒,是否已經(jīng)被取消等逻杖。每個Node結(jié)點內(nèi)部關(guān)聯(lián)其前繼結(jié)點prev和后繼結(jié)點next奋岁,這樣可以方便線程釋放鎖后快速喚醒下一個在等待的線程,Node是AQS的內(nèi)部類荸百,其數(shù)據(jù)結(jié)構(gòu)如下:
static final class Node {
? ? //共享模式
? ? static final Node SHARED = new Node();
? ? //獨占模式
? ? static final Node EXCLUSIVE = null;
? ? //標識線程已處于結(jié)束狀態(tài)
? ? static final int CANCELLED =? 1;
? ? //等待被喚醒狀態(tài)
? ? static final int SIGNAL? ? = -1;
? ? //條件狀態(tài)闻伶,
? ? static final int CONDITION = -2;
? ? //在共享模式中使用表示獲得的同步狀態(tài)會被傳播
? ? static final int PROPAGATE = -3;
? ? //等待狀態(tài),存在CANCELLED、SIGNAL够话、CONDITION蓝翰、PROPAGATE 4種? ??
????volatile int waitStatus;
? ? //同步隊列中前驅(qū)結(jié)點
? ? volatile Node prev;
? ? //同步隊列中后繼結(jié)點
? ? volatile Node next;
? ? //請求鎖的線程
? ? volatile Thread thread;
? ? //等待隊列中的后繼結(jié)點,這個與Condition有關(guān)女嘲,稍后會分析
? ? Node nextWaiter;
? ? //判斷是否為共享模式
? ? final boolean isShared() {
? ? ? ? return nextWaiter == SHARED;
? ? }
? ? //獲取前驅(qū)結(jié)點
? ? final Node predecessor() throws NullPointerException {
? ? ? ? Node p = prev;
? ? ? ? if (p == null)
? ? ? ? ? ? throw new NullPointerException();
? ? ? ? else
? ? ? ? ? ? return p;
? ? }
? ? //.....
}
其中SHARED和EXCLUSIVE常量分別代表共享模式和獨占模式畜份,所謂共享模式是一個鎖允許多條線程同時操作,如信號量Semaphore采用的就是基于AQS的共享模式實現(xiàn)的欣尼,而獨占模式則是同一個時間段只能有一個線程對共享資源進行操作爆雹,多余的請求線程需要排隊等待,如ReentranLock。變量waitStatus則表示當前被封裝成Node結(jié)點的等待狀態(tài)顶别,共有4種取值CANCELLED谷徙、SIGNAL、CONDITION驯绎、PROPAGATE完慧。
CANCELLED:值為1,在同步隊列中等待的線程等待超時或被中斷剩失,需要從同步隊列中取消該Node的結(jié)點屈尼,其結(jié)點的waitStatus為CANCELLED,即結(jié)束狀態(tài)拴孤,進入該狀態(tài)后的結(jié)點將不會再變化脾歧。
SIGNAL:值為-1,被標識為該等待喚醒狀態(tài)的后繼結(jié)點演熟,當其前繼結(jié)點的線程釋放了同步鎖或被取消鞭执,將會通知該后繼結(jié)點的線程執(zhí)行。說白了芒粹,就是處于喚醒狀態(tài)兄纺,只要前繼結(jié)點釋放鎖,就會通知標識為SIGNAL狀態(tài)的后繼結(jié)點的線程執(zhí)行化漆。
CONDITION:值為-2估脆,與Condition相關(guān),該標識的結(jié)點處于等待隊列中座云,結(jié)點的線程等待在Condition上疙赠,當其他線程調(diào)用了Condition的signal()方法后,CONDITION狀態(tài)的結(jié)點將從等待隊列轉(zhuǎn)移到同步隊列中朦拖,等待獲取同步鎖圃阳。
PROPAGATE:值為-3,與共享模式相關(guān)璧帝,在共享模式中限佩,該狀態(tài)標識結(jié)點的線程處于可運行狀態(tài)。
0狀態(tài):值為0裸弦,代表初始化狀態(tài)。
pre和next作喘,分別指向當前Node結(jié)點的前驅(qū)結(jié)點和后繼結(jié)點理疙,thread變量存儲的請求鎖的線程。nextWaiter泞坦,與Condition相關(guān)窖贤,代表等待隊列中的后繼結(jié)點,關(guān)于這點這里暫不深入,后續(xù)會有更詳細的分析赃梧,嗯滤蝠,到此我們對Node結(jié)點的數(shù)據(jù)結(jié)構(gòu)也就比較清晰了∈卩郑總之呢物咳,AQS作為基礎(chǔ)組件,對于鎖的實現(xiàn)存在兩種不同的模式蹄皱,即共享模式(如Semaphore)和獨占模式(如ReetrantLock)览闰,無論是共享模式還是獨占模式的實現(xiàn)類,其內(nèi)部都是基于AQS實現(xiàn)的巷折,也都維持著一個虛擬的同步隊列压鉴,當請求鎖的線程超過現(xiàn)有模式的限制時,會將線程包裝成Node結(jié)點并將線程當前必要的信息存儲到node結(jié)點中锻拘,然后加入同步隊列等會獲取鎖油吭,而這系列操作都有AQS協(xié)助我們完成,這也是作為基礎(chǔ)組件的原因署拟,無論是Semaphore還是ReetrantLock婉宰,其內(nèi)部絕大多數(shù)方法都是間接調(diào)用AQS完成的,下面是AQS整體類圖結(jié)構(gòu):
這里以ReentrantLock為例芯丧,簡單講解ReentrantLock與AQS的關(guān)系:
AbstractOwnableSynchronizer:抽象類芍阎,定義了存儲獨占當前鎖的線程和獲取的方法。
AbstractQueuedSynchronizer:抽象類缨恒,AQS框架核心類谴咸,其內(nèi)部以虛擬隊列的方式管理線程的鎖獲取與鎖釋放,其中獲取鎖(tryAcquire方法)和釋放鎖(tryRelease方法)并沒有提供默認實現(xiàn)骗露,需要子類重寫這兩個方法實現(xiàn)具體邏輯岭佳,目的是使開發(fā)人員可以自由定義獲取鎖以及釋放鎖的方式。
Node:AbstractQueuedSynchronizer 的內(nèi)部類萧锉,用于構(gòu)建虛擬隊列(鏈表雙向鏈表)珊随,管理需要獲取鎖的線程。
Sync:抽象類柿隙,是ReentrantLock的內(nèi)部類叶洞,繼承自AbstractQueuedSynchronizer,實現(xiàn)了釋放鎖的操作(tryRelease()方法)禀崖,并提供了lock抽象方法衩辟,由其子類實現(xiàn)。
NonfairSync:是ReentrantLock的內(nèi)部類波附,繼承自Sync艺晴,非公平鎖的實現(xiàn)類昼钻。
FairSync:是ReentrantLock的內(nèi)部類,繼承自Sync封寞,公平鎖的實現(xiàn)類然评。
ReentrantLock:實現(xiàn)了Lock接口的,其內(nèi)部類有Sync狈究、NonfairSync碗淌、FairSync,在創(chuàng)建時可以根據(jù)fair參數(shù)決定創(chuàng)建NonfairSync(默認非公平鎖)還是FairSync谦炒。
ReentrantLock內(nèi)部存在3個實現(xiàn)類贯莺,分別是Sync、NonfairSync宁改、FairSync缕探,其中Sync繼承自AQS實現(xiàn)了解鎖tryRelease()方法,而NonfairSync(非公平鎖)还蹲、?FairSync(公平鎖)則繼承自Sync爹耗,實現(xiàn)了獲取鎖的tryAcquire()方法,ReentrantLock的所有方法調(diào)用都通過間接調(diào)用AQS和Sync類及其子類來完成的谜喊。從上述類圖可以看出AQS是一個抽象類潭兽,但請注意其源碼中并沒一個抽象的方法,這是因為AQS只是作為一個基礎(chǔ)組件斗遏,并不希望直接作為直接操作類對外輸出山卦,而更傾向于作為基礎(chǔ)組件诵次,為真正的實現(xiàn)類提供基礎(chǔ)設(shè)施逾一,如構(gòu)建同步隊列箱玷,控制同步狀態(tài)等,事實上,從設(shè)計模式角度來看扩灯,AQS采用的模板模式的方式構(gòu)建的,其內(nèi)部除了提供并發(fā)操作核心方法以及同步隊列操作外捻撑,還提供了一些模板方法讓子類自己實現(xiàn),如加鎖操作以及解鎖操作,為什么這么做犁河?這是因為AQS作為基礎(chǔ)組件,封裝的是核心并發(fā)操作,但是實現(xiàn)上分為兩種模式,即共享模式與獨占模式狭园,而這兩種模式的加鎖與解鎖實現(xiàn)方式是不一樣的,但AQS只關(guān)注內(nèi)部公共方法實現(xiàn)并不關(guān)心外部不同模式的實現(xiàn),所以提供了模板方法給子類使用,也就是說實現(xiàn)獨占鎖冤留,如ReentrantLock需要自己實現(xiàn)tryAcquire()方法和tryRelease()方法糯而,而實現(xiàn)共享模式的Semaphore烘豹,則需要實現(xiàn)tryAcquireShared()方法和tryReleaseShared()方法祭芦,這樣做的好處是顯而易見的,無論是共享模式還是獨占模式,其基礎(chǔ)的實現(xiàn)都是同一套組件(AQS)避矢,只不過是加鎖解鎖的邏輯不同罷了卸勺,更重要的是如果我們需要自定義鎖的話碍庵,也變得非常簡單,只需要選擇不同的模式實現(xiàn)不同的加鎖和解鎖的模板方法即可,AQS提供給獨占模式和共享模式的模板方法如下:
//AQS中提供的主要模板方法囤攀,由子類實現(xiàn)。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
? ? //獨占模式下獲取鎖的方法
????protected boolean tryAcquire(int arg) {
? ? ? ? throw new UnsupportedOperationException();
? ? }
? ? //獨占模式下解鎖的方法
? ? protected boolean tryRelease(int arg) {
? ? ? ? throw new UnsupportedOperationException();
? ? }
? ? //共享模式下獲取鎖的方法
? ? protected int tryAcquireShared(int arg) {
? ? ? ? throw new UnsupportedOperationException();
? ? }
? ? //共享模式下解鎖的方法
? ? protected boolean tryReleaseShared(int arg) {
? ? ? ? throw new UnsupportedOperationException();
? ? }
? ? //判斷是否為持有獨占鎖
? ? protected boolean isHeldExclusively() {
? ? ? ? throw new UnsupportedOperationException();
? ? }
}
在了解AQS的原理概要后向抢,下面我們就基于ReetrantLock進一步分析AQS的實現(xiàn)過程,這也是ReetrantLock的內(nèi)部實現(xiàn)原理。
基于ReetrantLock分析AQS獨占模式實現(xiàn)過程
ReetrantLock中非公平鎖
AQS同步器的實現(xiàn)依賴于內(nèi)部的同步隊列(FIFO的雙向鏈表對列)完成對同步狀態(tài)(state)的管理,當前線程獲取鎖(同步狀態(tài))失敗時,AQS會將該線程以及相關(guān)等待信息包裝成一個節(jié)點(Node)并將其加入同步隊列,同時會阻塞當前線程,當同步狀態(tài)釋放時,會將頭結(jié)點head中的線程喚醒,讓其嘗試獲取同步狀態(tài)。關(guān)于同步隊列和Node結(jié)點彤枢,前面我們已進行了較為詳細的分析瓷们,這里重點分析一下獲取同步狀態(tài)和釋放同步狀態(tài)以及如何加入隊列的具體操作,這里從ReetrantLock入手分析AQS的具體實現(xiàn),我們先以非公平鎖為例進行分析。
//默認構(gòu)造,創(chuàng)建非公平鎖NonfairSync
public ReentrantLock() {
? ? sync = new NonfairSync();
}
//根據(jù)傳入?yún)?shù)創(chuàng)建鎖類型
public ReentrantLock(boolean fair) {
? ? sync = fair ? new FairSync() : new NonfairSync();
}
//加鎖操作
public void lock() {
? ? sync.lock();
}
前面說過sync是個抽象類爆侣,存在兩個不同的實現(xiàn)子類,這里從非公平鎖入手,看看其實現(xiàn):
/**
* 非公平鎖實現(xiàn)
*/
static final class NonfairSync extends Sync {
? ? //加鎖
? ? final void lock() {
? ? ? ? //執(zhí)行CAS操作羹蚣,獲取同步狀態(tài)
? ? ? ? if (compareAndSetState(0, 1))
? ? ? ? ? ? //成功則將獨占鎖線程設(shè)置為當前線程
? ? ? ? ? ? setExclusiveOwnerThread(Thread.currentThread());
? ? ? ? else
? ? ? ? ? ? //否則再次請求同步狀態(tài)
? ? ? ? ? ? acquire(1);
? ? }
}
這里獲取鎖時,首先對同步狀態(tài)執(zhí)行CAS操作,嘗試把state的狀態(tài)從0設(shè)置為1,如果返回true則代表獲取同步狀態(tài)成功嘱函,也就是當前線程獲取鎖成函似,可操作臨界資源堂氯,如果返回false,則表示已有線程持有該同步狀態(tài)(其值為1),獲取鎖失敗,注意這里存在并發(fā)的情景,也就是可能同時存在多個線程設(shè)置state變量么翰,因此是CAS操作保證了state變量操作的原子性码耐。返回false后束铭,執(zhí)行?acquire(1)方法,該方法是AQS中的方法渴语,它對中斷不敏感,即使線程獲取同步狀態(tài)失敗驾凶,進入同步隊列牙甫,后續(xù)對該線程執(zhí)行中斷操作也不會從同步隊列中移出,方法如下:
public final void acquire(int arg) {
? ? //再次嘗試獲取同步狀態(tài)
? ? if (!tryAcquire(arg) &&
? ? ? ? acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
? ? ? ? selfInterrupt();
}
這里傳入?yún)?shù)arg表示要獲取同步狀態(tài)后設(shè)置的值(即要設(shè)置state的值)调违,因為要獲取鎖窟哺,而status為0時是釋放鎖,1則是獲取鎖技肩,所以這里一般傳遞參數(shù)為1且轨,進入方法后首先會執(zhí)行tryAcquire(arg)方法浮声,在前面分析過該方法在AQS中并沒有具體實現(xiàn),而是交由子類實現(xiàn)旋奢,因此該方法是由ReetrantLock類內(nèi)部實現(xiàn)的:
//NonfairSync類
static final class NonfairSync extends Sync {
?protected final boolean tryAcquire(int acquires) {
? ? ? ? return nonfairTryAcquire(acquires);
? ? }
}
//Sync類
abstract static class Sync extends AbstractQueuedSynchronizer {
? //nonfairTryAcquire方法
? final boolean nonfairTryAcquire(int acquires) {
? ? ? final Thread current = Thread.currentThread();
? ? ? int c = getState();
? ? ? //判斷同步狀態(tài)是否為0泳挥,并嘗試再次獲取同步狀態(tài)
? ? ? if (c == 0) {
? ? ? ? ? //執(zhí)行CAS操作
? ? ? ? ? if (compareAndSetState(0, acquires)) {
? ? ? ? ? ? ? setExclusiveOwnerThread(current);
? ? ? ? ? ? ? return true;
? ? ? ? ? }
? ? ? }
? ? ? //如果當前線程已獲取鎖,屬于重入鎖至朗,再次獲取鎖后將status值加1
? ? ? else if (current == getExclusiveOwnerThread()) {
? ? ? ? ? int nextc = c + acquires;
? ? ? ? ? if (nextc < 0) // overflow
? ? ? ? ? ? ? throw new Error("Maximum lock count exceeded");
? ? ? ? ? //設(shè)置當前同步狀態(tài)屉符,當前只有一個線程持有鎖,因為不會發(fā)生線程安全問題锹引,可以直接執(zhí)行 setState(nextc);
? ? ? ? ? setState(nextc);
? ? ? ? ? return true;
? ? ? }
? ? ? return false;
? }
? //省略其他代碼
}
從代碼執(zhí)行流程可以看出矗钟,這里做了兩件事,一是嘗試再次獲取同步狀態(tài)嫌变,如果獲取成功則將當前線程設(shè)置為OwnerThread吨艇,否則失敗,二是判斷當前線程current是否為OwnerThread腾啥,如果是則屬于重入鎖东涡,state自增1,并獲取鎖成功倘待,返回true软啼,反之失敗,返回false延柠,也就是tryAcquire(arg)執(zhí)行失敗,返回false锣披。需要注意的是nonfairTryAcquire(int acquires)內(nèi)部使用的是CAS原子性操作設(shè)置state值贞间,可以保證state的更改是線程安全的,因此只要任意一個線程調(diào)用nonfairTryAcquire(int acquires)方法并設(shè)置成功即可獲取鎖雹仿,不管該線程是新到來的還是已在同步隊列的線程增热,畢竟這是非公平鎖,并不保證同步隊列中的線程一定比新到來線程請求(可能是head結(jié)點剛釋放同步狀態(tài)然后新到來的線程恰好獲取到同步狀態(tài))先獲取到鎖胧辽,這點跟后面還會講到的公平鎖不同峻仇。ok~,接著看之前的方法acquire(int arg):
public final void acquire(int arg) {
? ? //再次嘗試獲取同步狀態(tài)
? ? if (!tryAcquire(arg) &&
? ? ? ? acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
? ? ? ? selfInterrupt();
}
如果tryAcquire(arg)返回true邑商,acquireQueued自然不會執(zhí)行摄咆,這是最理想的,因為畢竟當前線程已獲取到鎖人断,如果tryAcquire(arg)返回false吭从,則會執(zhí)行addWaiter(Node.EXCLUSIVE)進行入隊操作,由于ReentrantLock屬于獨占鎖氛濒,因此結(jié)點類型為Node.EXCLUSIVE假褪,下面看看addWaiter方法具體實現(xiàn):
private Node addWaiter(Node mode) {
? ? //將請求同步狀態(tài)失敗的線程封裝成結(jié)點
? ? Node node = new Node(Thread.currentThread(), mode);
? ? Node pred = tail;
? ? //如果是第一個結(jié)點加入肯定為空,跳過漱牵。
? ? //如果非第一個結(jié)點則直接執(zhí)行CAS入隊操作,嘗試在尾部快速添加
? ? if (pred != null) {
? ? ? ? node.prev = pred;
? ? ? ? //使用CAS執(zhí)行尾部結(jié)點替換步做,嘗試在尾部快速添加
? ? ? ? if (compareAndSetTail(pred, node)) {
? ? ? ? ? ? pred.next = node;
? ? ? ? ? ? return node;
? ? ? ? }
? ? }
? ? //如果第一次加入或者CAS操作沒有成功執(zhí)行enq入隊操作
? ? enq(node);
? ? return node;
}
創(chuàng)建了一個Node.EXCLUSIVE類型Node結(jié)點用于封裝線程及其相關(guān)信息副渴,其中tail是AQS的成員變量,指向隊尾(這點前面的我們分析過AQS維持的是一個雙向的鏈表結(jié)構(gòu)同步隊列)全度,如果是第一個結(jié)點煮剧,則為tail肯定為空,那么將執(zhí)行enq(node)操作讼载,如果非第一個結(jié)點即tail指向不為null轿秧,直接嘗試執(zhí)行CAS操作加入隊尾,如果CAS操作失敗還是會執(zhí)行enq(node)咨堤,繼續(xù)看enq(node):
private Node enq(final Node node) {
? ? //死循環(huán)
? ? for (;;) {
? ? ? ? Node t = tail;
? ? ? ? //如果隊列為null菇篡,即沒有頭結(jié)點
? ? ? ? if (t == null) { // Must initialize
? ? ? ? ? ? //創(chuàng)建并使用CAS設(shè)置頭結(jié)點
? ? ? ? ? ? if (compareAndSetHead(new Node()))
? ? ? ? ? ? ? ? tail = head;
? ? ? ? ? ? } else {//隊尾添加新結(jié)點
? ? ? ? ? ????? node.prev = t;
? ? ? ? ? ? ???? if (compareAndSetTail(t, node)) {
? ? ? ? ? ? ? ? ???? t.next = node;
? ? ? ? ? ? ? ? ???? return t;
? ? ? ? ? ????? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
這個方法使用一個死循環(huán)進行CAS操作,可以解決多線程并發(fā)問題一喘。這里做了兩件事驱还,一是如果還沒有初始同步隊列則創(chuàng)建新結(jié)點并使用compareAndSetHead設(shè)置頭結(jié)點,tail也指向head凸克,二是隊列已存在议蟆,則將新結(jié)點node添加到隊尾。注意這兩個步驟都存在同一時間多個線程操作的可能萎战,如果有一個線程修改head和tail成功咐容,那么其他線程將繼續(xù)循環(huán),直到修改成功蚂维,這里使用CAS原子操作進行頭結(jié)點設(shè)置和尾結(jié)點tail替換可以保證線程安全戳粒,從這里也可以看出head結(jié)點本身不存在任何數(shù)據(jù),它只是作為一個牽頭結(jié)點虫啥,而tail永遠指向尾部結(jié)點(前提是隊列不為null)蔚约。
添加到同步隊列后,結(jié)點就會進入一個自旋過程涂籽,即每個結(jié)點都在觀察時機待條件滿足獲取同步狀態(tài)苹祟,然后從同步隊列退出并結(jié)束自旋,回到之前的acquire()方法评雌,自旋過程是在acquireQueued(addWaiter(Node.EXCLUSIVE), arg))方法中執(zhí)行的树枫,代碼如下:
final boolean acquireQueued(final Node node, int arg) {
? ? boolean failed = true;
? ? try {
? ? ? ? boolean interrupted = false;
? ? ? ? //自旋,死循環(huán)
? ? ? ? for (;;) {
? ? ? ? ? ? //獲取前驅(qū)結(jié)點
? ? ? ? ? ? final Node p = node.predecessor();
? ? ? ? ? ? 當且僅當p為頭結(jié)點才嘗試獲取同步狀態(tài)
? ? ? ? ? ? if (p == head && tryAcquire(arg)) {
? ? ? ? ? ? ? ? //將node設(shè)置為頭結(jié)點
? ? ? ? ? ? ? ? setHead(node);
? ? ? ? ? ? ? ? //清空原來頭結(jié)點的引用便于GC
? ? ? ? ? ? ? ? p.next = null; // help GC
? ? ? ? ? ? ? ? failed = false;
? ? ? ? ? ? ? ? return interrupted;
? ? ? ? ? ? }
? ? ? ? ? ? //如果前驅(qū)結(jié)點不是head景东,判斷是否掛起線程
? ? ? ? ? ? if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? parkAndCheckInterrupt())
? ? ? ? ? ? ? ? interrupted = true;
? ? ? ? }
? ? } finally {
? ? ? ? if (failed)
? ? ? ? ? ? //最終都沒能獲取同步狀態(tài)团赏,結(jié)束該線程的請求
? ? ? ? ? ? cancelAcquire(node);
? ? }
}
當前線程在自旋(死循環(huán))中獲取同步狀態(tài),當且僅當前驅(qū)結(jié)點為頭結(jié)點才嘗試獲取同步狀態(tài)耐薯,這符合FIFO的規(guī)則舔清,即先進先出丝里,其次head是當前獲取同步狀態(tài)的線程結(jié)點,只有當head釋放同步狀態(tài)喚醒后繼結(jié)點体谒,后繼結(jié)點才有可能獲取到同步狀態(tài)杯聚,因此后繼結(jié)點在其前繼結(jié)點為head時,才進行嘗試獲取同步狀態(tài)抒痒,其他時刻將被掛起幌绍。進入if語句后調(diào)用setHead(node)方法,將當前線程結(jié)點設(shè)置為head:
//設(shè)置為頭結(jié)點
private void setHead(Node node) {
? ? ? ? head = node;
? ? ? ? //清空結(jié)點數(shù)據(jù)
? ? ? ? node.thread = null;
? ? ? ? node.prev = null;
}
設(shè)置為node結(jié)點被設(shè)置為head后故响,其thread信息和前驅(qū)結(jié)點將被清空傀广,因為該線程已獲取到同步狀態(tài)(鎖),正在執(zhí)行了彩届,也就沒有必要存儲相關(guān)信息了伪冰,head只有保存指向后繼結(jié)點的指針即可,便于head結(jié)點釋放同步狀態(tài)后喚醒后繼結(jié)點樟蠕,執(zhí)行結(jié)果如下圖:
從圖可知更新head結(jié)點的指向贮聂,將后繼結(jié)點的線程喚醒并獲取同步狀態(tài),調(diào)用setHead(node)將其替換為head結(jié)點寨辩,清除相關(guān)無用數(shù)據(jù)吓懈。當然如果前驅(qū)結(jié)點不是head,那么執(zhí)行如下:
//如果前驅(qū)結(jié)點不是head靡狞,判斷是否掛起線程
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
? ? ? interrupted = true;
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
? ? ? ? //獲取當前結(jié)點的等待狀態(tài)
? ? ? ? int ws = pred.waitStatus;
? ? ? ? //如果為等待喚醒(SIGNAL)狀態(tài)則返回true
? ? ? ? if (ws == Node.SIGNAL)
? ? ? ? ? ? return true;
? ? ? ? //如果ws>0 則說明是結(jié)束狀態(tài)耻警,
? ? ? ? //遍歷前驅(qū)結(jié)點直到找到?jīng)]有結(jié)束狀態(tài)的結(jié)點
? ? ? ? if (ws > 0) {
? ? ? ? ? ? do {
? ? ? ? ? ? ? ? node.prev = pred = pred.prev;
? ? ? ? ? ? } while (pred.waitStatus > 0);
? ? ? ? ? ? pred.next = node;
? ? ? ? } else {
? ? ? ? ? ? //如果ws小于0又不是SIGNAL狀態(tài),
? ? ? ? ? ? //則將其設(shè)置為SIGNAL狀態(tài)甸怕,代表該結(jié)點的線程正在等待喚醒榕栏。
? ? ? ? ? ? compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
? ? ? ? }
? ? ? ? return false;
? ? }
private final boolean parkAndCheckInterrupt() {
? ? ? ? //將當前線程掛起
? ? ? ? LockSupport.park(this);
? ? ? ? //獲取線程中斷狀態(tài),interrupted()是判斷當前中斷狀態(tài),
? ? ? ? //并非中斷線程蕾各,因此可能true也可能false,并返回
? ? ? ? return Thread.interrupted();
}
shouldParkAfterFailedAcquire()方法的作用是判斷當前結(jié)點的前驅(qū)結(jié)點是否為SIGNAL狀態(tài)(即等待喚醒狀態(tài)),如果是則返回true庆揪。如果結(jié)點的ws為CANCELLED狀態(tài)(值為1>0),即結(jié)束狀態(tài)式曲,則說明該前驅(qū)結(jié)點已沒有用應該從同步隊列移除,執(zhí)行while循環(huán)缸榛,直到尋找到非CANCELLED狀態(tài)的結(jié)點吝羞。倘若前驅(qū)結(jié)點的ws值不為CANCELLED,也不為SIGNAL(當從Condition的條件等待隊列轉(zhuǎn)移到同步隊列時内颗,結(jié)點狀態(tài)為CONDITION因此需要轉(zhuǎn)換為SIGNAL)钧排,那么將其轉(zhuǎn)換為SIGNAL狀態(tài),等待被喚醒均澳。?
若shouldParkAfterFailedAcquire()方法返回true恨溜,即前驅(qū)結(jié)點為SIGNAL狀態(tài)同時又不是head結(jié)點符衔,那么使用parkAndCheckInterrupt()方法掛起當前線程,稱為WAITING狀態(tài)糟袁,需要等待一個unpark()操作來喚醒它判族,到此ReetrantLock內(nèi)部間接通過AQS的FIFO的同步隊列就完成了lock()操作,這里我們總結(jié)成邏輯流程圖:
關(guān)于獲取鎖的操作项戴,這里看看另外一種可中斷的獲取方式形帮,即調(diào)用ReentrantLock類的lockInterruptibly()或者tryLock()方法,最終它們都間接調(diào)用到doAcquireInterruptibly():
public final void acquireInterruptibly(int arg) throws InterruptedException {
????if (Thread.interrupted())
????????throw new InterruptedException();
????if (!tryAcquire(arg))
????????doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
? ? ? ? throws InterruptedException {
? ? ? ? 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;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ? parkAndCheckInterrupt())
? ? ? ? ? ? ? ? ? ? //直接拋異常周叮,中斷線程的同步狀態(tài)請求
? ? ? ? ? ? ? ? ? ? throw new InterruptedException();
? ? ? ? ? ? }
? ? ? ? } finally {
? ? ? ? ? ? if (failed)
? ? ? ? ? ? ? ? cancelAcquire(node);
? ? ? ? }
? ? }
最大的不同是:
if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ? parkAndCheckInterrupt())
? ? //直接拋異常辩撑,中斷線程的同步狀態(tài)請求
? ? ? throw new InterruptedException();
}
檢測到線程的中斷操作后,直接拋出異常仿耽,從而中斷線程的同步狀態(tài)請求合冀,移除同步隊列,ok~,加鎖流程到此氓仲。下面接著看unlock()操作:
//ReentrantLock類的unlock
public void unlock() {
? ? sync.release(1);
}
//AQS類的release()方法
public final boolean release(int arg) {
? ? //嘗試釋放鎖
? ? if (tryRelease(arg)) {
? ? ? ? Node h = head;
? ? ? ? if (h != null && h.waitStatus != 0)
? ? ? ? ? ? //喚醒后繼結(jié)點的線程
? ? ? ? ? ? unparkSuccessor(h);
? ? ? ? return true;
? ? }
? ? return false;
}
//ReentrantLock類中的內(nèi)部類Sync實現(xiàn)的tryRelease(int releases)
?protected final boolean tryRelease(int releases) {
? ? ? int c = getState() - releases;
? ? ? if (Thread.currentThread() != getExclusiveOwnerThread())
? ? ? ? ? throw new IllegalMonitorStateException();
? ? ? boolean free = false;
? ? ? //判斷狀態(tài)是否為0水慨,如果是則說明已釋放同步狀態(tài)
? ? ? if (c == 0) {
? ? ? ? ? free = true;
? ? ? ? ? //設(shè)置Owner為null
? ? ? ? ? setExclusiveOwnerThread(null);
? ? ? }
? ? ? //設(shè)置更新同步狀態(tài)
? ? ? setState(c);
? ? ? return free;
? }
釋放同步狀態(tài)的操作相對簡單些,tryRelease(int releases)方法是ReentrantLock類中內(nèi)部類自己實現(xiàn)的敬扛,因為AQS對于釋放鎖并沒有提供具體實現(xiàn)晰洒,必須由子類自己實現(xiàn)。釋放同步狀態(tài)后會使用unparkSuccessor(h)喚醒后繼結(jié)點的線程啥箭,這里看看unparkSuccessor(h):
private void unparkSuccessor(Node node) {
? ? //這里谍珊,node一般為當前線程所在的結(jié)點。
? ? int ws = node.waitStatus;
? ? if (ws < 0)//置零當前線程所在的結(jié)點狀態(tài)急侥,允許失敗砌滞。
? ? ? ? compareAndSetWaitStatus(node, ws, 0);
? ? Node s = node.next;
????//找到下一個需要喚醒的結(jié)點s
? ? if (s == null || s.waitStatus > 0) {//如果為空或已取消
? ? ? ? s = null;
? ? ? ? for (Node t = tail; t != null && t != node; t = t.prev)
? ? ? ? ? ? if (t.waitStatus <= 0)//從這里可以看出,<=0的結(jié)點坏怪,都是還有效的結(jié)點贝润。
? ? ? ? ? ? ? ? s = t;
? ? }
? ? if (s != null)
? ? ? ? LockSupport.unpark(s.thread);//喚醒
}
從代碼執(zhí)行操作來看,這里主要作用是用unpark()喚醒同步隊列中最前邊未放棄線程(也就是狀態(tài)為CANCELLED的線程結(jié)點s)铝宵。此時打掘,回憶前面分析進入自旋的函數(shù)acquireQueued(),s結(jié)點的線程被喚醒后鹏秋,會進入acquireQueued()函數(shù)的if (p == head && tryAcquire(arg))的判斷尊蚁,如果p!=head也不會有影響,因為它會執(zhí)行shouldParkAfterFailedAcquire()侣夷,由于s通過unparkSuccessor()操作后已是同步隊列中最前邊未放棄的線程結(jié)點横朋,那么通過shouldParkAfterFailedAcquire()內(nèi)部對結(jié)點狀態(tài)的調(diào)整,s也必然會成為head的next結(jié)點百拓,因此再次自旋時p==head就成立了琴锭,然后s把自己設(shè)置成head結(jié)點晰甚,表示自己已經(jīng)獲取到資源了,最終acquire()也返回了祠够,這就是獨占鎖釋放的過程压汪。?
ok~,關(guān)于獨占模式的加鎖和釋放鎖的過程到這就分析完古瓤,總之呢止剖,在AQS同步器中維護著一個同步隊列,當線程獲取同步狀態(tài)失敗后落君,將會被封裝成Node結(jié)點穿香,加入到同步隊列中并進行自旋操作,當當前線程結(jié)點的前驅(qū)結(jié)點為head時绎速,將嘗試獲取同步狀態(tài)皮获,獲取成功將自己設(shè)置為head結(jié)點。在釋放同步狀態(tài)時纹冤,則通過調(diào)用子類(ReetrantLock中的Sync內(nèi)部類)的tryRelease(int releases)方法釋放同步狀態(tài)洒宝,釋放成功則喚醒后繼結(jié)點的線程。
ReetrantLock中公平鎖
了解完ReetrantLock中非公平鎖的實現(xiàn)后萌京,我們再來看看公平鎖雁歌。與非公平鎖不同的是,在獲取鎖的時知残,公平鎖的獲取順序是完全遵循時間上的FIFO規(guī)則靠瞎,也就是說先請求的線程一定會先獲取鎖,后來的線程肯定需要排隊求妹,這點與前面我們分析非公平鎖的nonfairTryAcquire(int acquires)方法實現(xiàn)有鎖不同乏盐,下面是公平鎖中tryAcquire()方法的實現(xiàn):
//公平鎖FairSync類中的實現(xiàn)
protected final boolean tryAcquire(int acquires) {
? ? ? ? ? ? final Thread current = Thread.currentThread();
? ? ? ? ? ? int c = getState();
? ? ? ? ? ? if (c == 0) {
? ? ? ? ? ? //注意!制恍!這里先判斷同步隊列是否存在結(jié)點
? ? ? ? ? ? ? ? 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;
? ? ? ? }
該方法與nonfairTryAcquire(int acquires)方法唯一的不同是在使用CAS設(shè)置嘗試設(shè)置state值前父能,調(diào)用了hasQueuedPredecessors()判斷同步隊列是否存在結(jié)點,如果存在必須先執(zhí)行完同步隊列中結(jié)點的線程净神,當前線程進入等待狀態(tài)何吝。這就是非公平鎖與公平鎖最大的區(qū)別,即公平鎖在線程請求到來時先會判斷同步隊列是否存在結(jié)點强挫,如果存在先執(zhí)行同步隊列中的結(jié)點線程,當前線程將封裝成node加入同步隊列等待薛躬。而非公平鎖呢俯渤,當線程請求到來時,不管同步隊列是否存在線程結(jié)點型宝,直接嘗試獲取同步狀態(tài)八匠,獲取成功直接訪問共享資源絮爷,但請注意在絕大多數(shù)情況下,非公平鎖才是我們理想的選擇梨树,畢竟從效率上來說非公平鎖總是勝于公平鎖坑夯。?
以上便是ReentrantLock的內(nèi)部實現(xiàn)原理,這里我們簡單進行小結(jié)抡四,重入鎖ReentrantLock柜蜈,是一個基于AQS并發(fā)框架的并發(fā)控制類,其內(nèi)部實現(xiàn)了3個類指巡,分別是Sync淑履、NoFairSync以及FairSync類,其中Sync繼承自AQS藻雪,實現(xiàn)了釋放鎖的模板方法tryRelease(int)秘噪,而NoFairSync和FairSync都繼承自Sync,實現(xiàn)各種獲取鎖的方法tryAcquire(int)勉耀。ReentrantLock的所有方法實現(xiàn)幾乎都間接調(diào)用了這3個類指煎,因此當我們在使用ReentrantLock時,大部分使用都是在間接調(diào)用AQS同步器中的方法便斥,這就是ReentrantLock的內(nèi)部實現(xiàn)原理,最后給出張類圖結(jié)構(gòu):
關(guān)于synchronized 與ReentrantLock
在JDK 1.6之后至壤,虛擬機對于synchronized關(guān)鍵字進行整體優(yōu)化后,在性能上synchronized與ReentrantLock已沒有明顯差距椭住,因此在使用選擇上崇渗,需要根據(jù)場景而定,大部分情況下我們依然建議是synchronized關(guān)鍵字京郑,原因之一是使用方便語義清晰宅广,二是性能上虛擬機已為我們自動優(yōu)化。而ReentrantLock提供了多樣化的同步特性些举,如超時獲取鎖跟狱、可以被中斷獲取鎖(synchronized的同步是不能中斷的)、等待喚醒機制的多個條件變量(Condition)等户魏,因此當我們確實需要使用到這些功能是驶臊,可以選擇ReentrantLock。