synchronized實(shí)現(xiàn)原理及ReentrantLock源碼
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??曹自標(biāo)
來(lái)源 |? urlify.cn/bqqqey
66套java從入門(mén)到精通實(shí)戰(zhàn)課程分享
synchronized的作用范圍
public?class?SynchronizedTest?{
????//?實(shí)例方法,方法訪問(wèn)標(biāo)志ACC_SYNCHRONIZED,鎖對(duì)象是對(duì)象實(shí)例
????public?synchronized?void?test1(){}
????//?靜態(tài)方法,方法訪問(wèn)標(biāo)志ACC_SYNCHRONIZED,鎖對(duì)象是MetaSpace中的Class
????//?相當(dāng)于類的全局鎖,會(huì)鎖住所有調(diào)用該方法的線程
????public?synchronized?static?void?test2(){}
????public?void?test3()?{
????????//同步代碼塊,在代碼塊前增加monitorenter指令,代碼塊后增加monitorexit指令
????????SynchronizedTest?synchronizedTest?=?new?SynchronizedTest();
????????synchronized?(synchronizedTest)?{}
????????//?類鎖,效果等同于鎖靜態(tài)方法。代碼塊前后增加monitorenter、monitorexit指令
????????synchronized?(SynchronizedTest.class)?{}
????}
}
可jclasslib查看Acc_SYNCHRONIZED標(biāo)志和monitorenter、monitorexit指令
test1 方法:
Access?flags:?0x0021[public?synchronized]
test2 方法:
Access?flags:?0x0029[public?static?synchronized]
test3方法Code操作碼:
?0?new?#2?
?3?dup
?4?invokespecial?#3?>
?7?astore_1
?8?aload_1
?9?dup
10?astore_2
11?monitorenter
12?aload_2
13?monitorexit
14?goto?22?(+8)
17?astore_3
18?aload_2
19?monitorexit
20?aload_3
21?athrow
22?ldc?#2?
24?dup
25?astore_2
26?monitorenter
27?aload_2
28?monitorexit
29?goto?39?(+10)
32?astore?4
34?aload_2
35?monitorexit
36?aload?4
38?athrow
39?return
synchronized實(shí)現(xiàn)
核心組件
Wait Set:哪些調(diào)用 wait方法被阻塞的線程被放置在這里
Contention List:競(jìng)爭(zhēng)隊(duì)列,所有請(qǐng)求鎖的線程首先被放在這個(gè)競(jìng)爭(zhēng)隊(duì)列中
Entry List:Contention List 中那些有資格成為候選資源的線程被移動(dòng)到 Entry List 中
OnDeck:任意時(shí)刻, 最多只有一個(gè)線程正在競(jìng)爭(zhēng)鎖資源,該線程被成為 OnDeck
Owner:當(dāng)前已經(jīng)獲取到所資源的線程被稱為 Owner
!Owner:當(dāng)前釋放鎖的線程
圖示過(guò)程:

解釋:
JVM 每次從隊(duì)列的尾部取出一個(gè)數(shù)據(jù)用于鎖競(jìng)爭(zhēng)候選者(OnDeck),但是并發(fā)情況下,ContentionList 會(huì)被大量的并發(fā)線程進(jìn)行 CAS 訪問(wèn),為了降低對(duì)尾部元素的競(jìng)爭(zhēng), JVM 會(huì)將一部分線程移動(dòng)到 EntryList 中作為候選競(jìng)爭(zhēng)線程。
Owner 線程會(huì)在 unlock 時(shí),將 ContentionList 中的部分線程遷移到 EntryList 中,并指定EntryList 中的某個(gè)線程為 OnDeck 線程(一般是最先進(jìn)去的那個(gè)線程)。
Owner 線程并不直接把鎖傳遞給 OnDeck 線程,而是把鎖競(jìng)爭(zhēng)的權(quán)利交給 OnDeck,OnDeck 需要重新競(jìng)爭(zhēng)鎖。這樣雖然犧牲了一些公平性,但是能極大的提升系統(tǒng)的吞吐量,在JVM 中,也把這種選擇行為稱之為“競(jìng)爭(zhēng)切換”。
OnDeck 線程獲取到鎖資源后會(huì)變?yōu)?Owner 線程,而沒(méi)有得到鎖資源的仍然停留在 EntryList中。如果 Owner 線程被 wait 方法阻塞,則轉(zhuǎn)移到 WaitSet 隊(duì)列中,直到某個(gè)時(shí)刻通過(guò) notify或者 notifyAll 喚醒,會(huì)重新進(jìn)去 EntryList 中。
處于 ContentionList、 EntryList、 WaitSet 中的線程都處于阻塞狀態(tài),該阻塞是由操作系統(tǒng)來(lái)完成的(Linux 內(nèi)核下采用 pthread_mutex_lock 內(nèi)核函數(shù)實(shí)現(xiàn)的)。
Synchronized 是非公平鎖。Synchronized 在線程進(jìn)入 ContentionList 時(shí), 等待的線程會(huì)先嘗試自旋獲取鎖,如果獲取不到就進(jìn)入 ContentionList,這明顯對(duì)于已經(jīng)進(jìn)入隊(duì)列的線程是不公平的,還有一個(gè)不公平的事情就是自旋獲取鎖的線程還可能直接搶占 OnDeck 線程的鎖資源。
參考:?https://blog.csdn.net/zqz_zqz/article/details/70233767每個(gè)對(duì)象都有個(gè) monitor 對(duì)象, 加鎖就是在競(jìng)爭(zhēng) monitor 對(duì)象,代碼塊加鎖是在前后分別加上 monitorenter 和 monitorexit 指令來(lái)實(shí)現(xiàn)的,方法加鎖是通過(guò)一個(gè)標(biāo)記位來(lái)判斷的
synchronized 是一個(gè)重量級(jí)操作,需要調(diào)用操作系統(tǒng)相關(guān)接口,性能是低效的,有可能給線程加鎖消耗的時(shí)間比有用操作消耗的時(shí)間更多。
Java1.6, synchronized 進(jìn)行了很多的優(yōu)化, 有適應(yīng)自旋、鎖消除、鎖粗化、輕量級(jí)鎖及偏向鎖等,效率有了本質(zhì)上的提高。在之后推出的 Java1.7 與 1.8 中,均對(duì)該關(guān)鍵字的實(shí)現(xiàn)機(jī)理做了優(yōu)化。引入了偏向鎖和輕量級(jí)鎖。都是在對(duì)象頭中有標(biāo)記位,不需要經(jīng)過(guò)操作系統(tǒng)加鎖。
鎖可以從偏向鎖升級(jí)到輕量級(jí)鎖,再升級(jí)到重量級(jí)鎖。這種升級(jí)過(guò)程叫做鎖膨脹;
JDK 1.6 中默認(rèn)是開(kāi)啟偏向鎖和輕量級(jí)鎖,可以通過(guò)-XX:-UseBiasedLocking 來(lái)禁用偏向鎖
ReentrantLock
ReentrantLock初始化時(shí),會(huì)new一個(gè)同步類(默認(rèn)非公平NonfairSync,當(dāng)傳入公平參數(shù)fair=true時(shí),則new公平類FairSync);而FairSync 和NonfairSync都繼承ReentrantLock中內(nèi)部類Sync,Sync則繼承同步器AbstractQueuedSynchronizer。UML圖如下(https://www.cnblogs.com/zhimingyang/p/5702752.html?截取):

Lock流程圖(非公平鎖示例)

源碼
ReentrantLock$NonfairSync#lock(),當(dāng)state為0,即compareAndSetState(0, 1)為true時(shí),獲得鎖;否則進(jìn)行下一步
ReentrantLock$NonfairSync#acquire() ——> AbstractQueuedSynchronizer#acquire() --> ReentrantLock$NonfairSync#tryAcquire() -->
ReentrantLock$Sync#nonfairTryAcquire(), 第2次嘗試獲取鎖在上面acquire方法中,還會(huì)調(diào)用addWaiter方法,將一個(gè)排他鎖加入隊(duì)列
public?class?ReentrantLock?implements?Lock,?java.io.Serializable?{
????
????abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{
????????final?boolean?nonfairTryAcquire(int?acquires)?{
????????????final?Thread?current?=?Thread.currentThread();
????????????int?c?=?getState();
????????????if?(c?==?0)?{
????????????????//第2次嘗試獲取鎖
????????????????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;
????????}
????}
????????
????static?final?class?NonfairSync?extends?Sync?{
????
????????final?void?lock()?{
????????????//?可不進(jìn)入隊(duì)列,直接搶鎖
????????????if?(compareAndSetState(0,?1))
????????????????setExclusiveOwnerThread(Thread.currentThread());
????????????else
????????????????acquire(1);
????????}
????
????????protected?final?boolean?tryAcquire(int?acquires)?{
????????????return?nonfairTryAcquire(acquires);
????????}
????}
}
public?abstract?class?AbstractQueuedSynchronizer
????extends?AbstractOwnableSynchronizer
????implements?java.io.Serializable?{
????
????public?final?void?acquire(int?arg)?{
????????//?步驟3,加入等待隊(duì)列,默認(rèn)排他鎖
????????if?(!tryAcquire(arg)?&&
????????????acquireQueued(addWaiter(Node.EXCLUSIVE),?arg))
????????????selfInterrupt();
????}
而繼續(xù)addWaiter、enq和acquireQueued則是實(shí)現(xiàn)以下圖示過(guò)程:
????private?Node?addWaiter(Node?mode)?{
????????Node?node?=?new?Node(Thread.currentThread(),?mode);
????????//?Try?the?fast?path?of?enq;?backup?to?full?enq?on?failure
????????Node?pred?=?tail;
????????if?(pred?!=?null)?{
????????????node.prev?=?pred;
????????????if?(compareAndSetTail(pred,?node))?{
????????????????pred.next?=?node;
????????????????return?node;
????????????}
????????}
????????//前置節(jié)點(diǎn)為null的臨界條件,第一個(gè)線程進(jìn)入等待隊(duì)列
????????enq(node);
????????return?node;
????}
前置節(jié)點(diǎn)為null的臨界條件,第一個(gè)線程進(jìn)入等待隊(duì)列,進(jìn)行初始化
????private?Node?enq(final?Node?node)?{
????????for?(;;)?{
????????????Node?t?=?tail;
????????????if?(t?==?null)?{?//?Must?initialize
????????????????//隊(duì)列初始化
????????????????if?(compareAndSetHead(new?Node()))
????????????????????tail?=?head;
????????????}?else?{
????????????????//雙向鏈表添加元素
????????????????node.prev?=?t;
????????????????if?(compareAndSetTail(t,?node))?{
????????????????????t.next?=?node;
????????????????????return?t;
????????????????}
????????????}
????????}
????}
????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;?//?help?GC
????????????????????failed?=?false;
????????????????????return?interrupted;
????????????????}
????????????????if?(shouldParkAfterFailedAcquire(p,?node)?&&
????????????????????parkAndCheckInterrupt())
????????????????????interrupted?=?true;
????????????}
????????}?finally?{
????????????if?(failed)
????????????????cancelAcquire(node);
????????}
????}
node屬性值介紹:
對(duì)應(yīng)源碼:
public?abstract?class?AbstractQueuedSynchronizer
????extends?AbstractOwnableSynchronizer
????implements?java.io.Serializable?{
????static?final?class?Node?{
????????
????????static?final?Node?EXCLUSIVE?=?null;
????????static?final?int?CANCELLED?=??1;
????????static?final?int?SIGNAL????=?-1;
????????static?final?int?CONDITION?=?-2;
????????static?final?int?PROPAGATE?=?-3;
????????volatile?int?waitStatus;
????????volatile?Node?prev;
????????volatile?Node?next;
????????volatile?Thread?thread;
????????Node?nextWaiter;
????????final?boolean?isShared()?{
????????????return?nextWaiter?==?SHARED;
????????}
????????final?Node?predecessor()?throws?NullPointerException?{
????????????Node?p?=?prev;
????????????if?(p?==?null)
????????????????throw?new?NullPointerException();
????????????else
????????????????return?p;
????????}
????????Node()?{????//?Used?to?establish?initial?head?or?SHARED?marker
????????}
????????Node(Thread?thread,?Node?mode)?{?????//?Used?by?addWaiter
????????????this.nextWaiter?=?mode;
????????????this.thread?=?thread;
????????}
????????Node(Thread?thread,?int?waitStatus)?{?//?Used?by?Condition
????????????this.waitStatus?=?waitStatus;
????????????this.thread?=?thread;
????????}
????}
}
重入鎖的實(shí)現(xiàn)
重入鎖的可重復(fù)進(jìn)入在以下代碼中實(shí)現(xiàn)(非公平鎖示例,公平鎖代碼一樣):
c > 0, 即有鎖,并且獲取鎖的線程就是當(dāng)前線程,則將state加1,并更新
final?boolean?nonfairTryAcquire(int?acquires)?{
????final?Thread?current?=?Thread.currentThread();
????int?c?=?getState();
????if?(c?==?0)?{
????????...
????}
????//?c?>?0
????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;
}
公平鎖和非公平鎖
第一處不公平地方(lock方法):
非公平鎖lock時(shí),如果發(fā)現(xiàn)沒(méi)有鎖了,即state為0,可以不管隊(duì)列,直接compareAndSetState,如果獲取true了(搶到鎖),直接獲得鎖,不用進(jìn)同步器中的隊(duì)列。
而公平鎖沒(méi)有此邏輯。
static?final?class?NonfairSync?extends?Sync?{
????final?void?lock()?{
????????//?可不進(jìn)入隊(duì)列,直接搶鎖
????????if?(compareAndSetState(0,?1))
????????????setExclusiveOwnerThread(Thread.currentThread());
????????else
????????????acquire(1);
????}
}
static?final?class?FairSync?extends?Sync?{
????final?void?lock()?{
????????acquire(1);
????}
}
第二處不公平的地方(tryAcquire):
非公平鎖tryAcquire方法會(huì)調(diào)用Sync#nonfairTryAcquire(),當(dāng)state為0,發(fā)現(xiàn)鎖被釋放時(shí),可直接搶鎖
公平鎖則必須滿足!hasQueuedPredecessors()條件,也即必須同步器中隊(duì)列沒(méi)有線程在等待,才去獲取鎖
static?final?class?NonfairSync?extends?Sync?{
????protected?final?boolean?tryAcquire(int?acquires)?{
????????return?nonfairTryAcquire(acquires);
????}
}
abstract?static?class?Sync?extends?AbstractQueuedSynchronizer?{
????final?boolean?nonfairTryAcquire(int?acquires)?{
????????final?Thread?current?=?Thread.currentThread();
????????int?c?=?getState();
????????if?(c?==?0)?{
????????????//發(fā)現(xiàn)鎖被釋放時(shí),可直接搶鎖
????????????if?(compareAndSetState(0,?acquires))?{
????????????????setExclusiveOwnerThread(current);
????????????????return?true;
????????????}
????????}
????????...
????}
}
公平鎖
static?final?class?FairSync?extends?Sync?{
????protected?final?boolean?tryAcquire(int?acquires)?{
????????final?Thread?current?=?Thread.currentThread();
????????int?c?=?getState();
????????if?(c?==?0)?{
????????????//?必須同步器中隊(duì)列沒(méi)有線程在等待,才去獲取鎖
????????????if?(!hasQueuedPredecessors()?&&
????????????????compareAndSetState(0,?acquires))?{
????????????????setExclusiveOwnerThread(current);
????????????????return?true;
????????????}
????????}
????????...
????}
}
第三處不公平地方,加入隊(duì)列時(shí),前置節(jié)點(diǎn)是頭節(jié)點(diǎn):
????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))?{
????????????????...
????????????????}
????????????}
????}
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出。
粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖
???

?長(zhǎng)按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?
