<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          synchronized實(shí)現(xiàn)原理及ReentrantLock源碼

          共 4195字,需瀏覽 9分鐘

           ·

          2020-12-24 00:47

          點(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ò)程:

          解釋:

          1. 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)線程。

          2. Owner 線程會(huì)在 unlock 時(shí),將 ContentionList 中的部分線程遷移到 EntryList 中,并指定EntryList 中的某個(gè)線程為 OnDeck 線程(一般是最先進(jìn)去的那個(gè)線程)。

          3. Owner 線程并不直接把鎖傳遞給 OnDeck 線程,而是把鎖競(jìng)爭(zhēng)的權(quán)利交給 OnDeck,OnDeck 需要重新競(jìng)爭(zhēng)鎖。這樣雖然犧牲了一些公平性,但是能極大的提升系統(tǒng)的吞吐量,在JVM 中,也把這種選擇行為稱之為“競(jìng)爭(zhēng)切換”。

          4. OnDeck 線程獲取到鎖資源后會(huì)變?yōu)?Owner 線程,而沒(méi)有得到鎖資源的仍然停留在 EntryList中。如果 Owner 線程被 wait 方法阻塞,則轉(zhuǎn)移到 WaitSet 隊(duì)列中,直到某個(gè)時(shí)刻通過(guò) notify或者 notifyAll 喚醒,會(huì)重新進(jìn)去 EntryList 中。

          5. 處于 ContentionList、 EntryList、 WaitSet 中的線程都處于阻塞狀態(tài),該阻塞是由操作系統(tǒng)來(lái)完成的(Linux 內(nèi)核下采用 pthread_mutex_lock 內(nèi)核函數(shù)實(shí)現(xiàn)的)。

          6. 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

          7. 每個(gè)對(duì)象都有個(gè) monitor 對(duì)象, 加鎖就是在競(jìng)爭(zhēng) monitor 對(duì)象,代碼塊加鎖是在前后分別加上 monitorenter 和 monitorexit 指令來(lái)實(shí)現(xiàn)的,方法加鎖是通過(guò)一個(gè)標(biāo)記位來(lái)判斷的

          8. synchronized 是一個(gè)重量級(jí)操作,需要調(diào)用操作系統(tǒng)相關(guān)接口,性能是低效的,有可能給線程加鎖消耗的時(shí)間比有用操作消耗的時(shí)間更多。

          9. 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)加鎖。

          10. 鎖可以從偏向鎖升級(jí)到輕量級(jí)鎖,再升級(jí)到重量級(jí)鎖。這種升級(jí)過(guò)程叫做鎖膨脹;

          11. 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流程圖(非公平鎖示例)

          源碼
          1. ReentrantLock$NonfairSync#lock(),當(dāng)state為0,即compareAndSetState(0, 1)為true時(shí),獲得鎖;否則進(jìn)行下一步

          2. ReentrantLock$NonfairSync#acquire() ——> AbstractQueuedSynchronizer#acquire() --> ReentrantLock$NonfairSync#tryAcquire() -->
            ReentrantLock$Sync#nonfairTryAcquire(), 第2次嘗試獲取鎖

          3. 在上面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?????????????????????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?????????????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)贊支持下哈?

          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉伊看人在线在线观看 | 三级视频久久 | 日韩中文网 | 国产美女被干网站 | 国产一级a毛一级a毛视频在线网站) |