1. 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從入門到精通實(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)是開啟偏向鎖和輕量級(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?截?。?/span>

        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從入門到入土學(xué)習(xí)路線圖

        ???

        ?長(zhǎng)按上方微信二維碼?2 秒


        感謝點(diǎn)贊支持下哈?

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 久9热| 性中国孕交 | 无码一区二区三区四 | 刘亦菲张开双腿流白 | 日本少妇被黑人粗大的猛激 |