1. Java | synchronized 不同情況下的對象頭測試

        共 9536字,需瀏覽 20分鐘

         ·

        2021-11-30 02:59

        synchronized 不同情況下的對象頭測試

        測試環(huán)境

        JDK:Oracle JDK 1.8.0_144

        代碼依賴:

        • junit-jupiter-engine:5.8.1

        • slf4j-simple:1.7.32

        • jol-core:0.16

        測試代碼

         1import?java.util.concurrent.TimeUnit;
        2import?org.junit.jupiter.api.Assertions;
        3import?org.junit.jupiter.api.Test;
        4import?org.openjdk.jol.info.ClassLayout;
        5import?org.slf4j.Logger;
        6import?org.slf4j.LoggerFactory;
        7
        8class?LockObject?{}
        9
        10class?SyncTest?{
        11
        12????private?static?final?Logger?log?=?LoggerFactory.getLogger(SyncTest.class);
        13
        14????@Test
        15??void?testSynchronizedLock()?throws?InterruptedException?{
        16????Object?lock?=?new?Object();
        17????syncLock(lock);
        18????Assertions.assertTrue(true);
        19??}
        20
        21??void?syncLock(Object?lock)?{
        22????log.info("加鎖前?{}",?ClassLayout.parseInstance(lock).toPrintable());
        23????synchronized?(lock)?{
        24????????log.info("加鎖中?{}",?ClassLayout.parseInstance(lock).toPrintable());
        25????}
        26????log.info("加鎖后?{}",?ClassLayout.parseInstance(lock).toPrintable());
        27??}
        28}

        測試情況

        這里通過改動 testSynchronizedLock 方法代碼進行測試,下面的測試情況只說明改動后的 testSynchronizedLock的代碼,其余代碼不再說明。因為只關(guān)注對象頭的變化,其余的值也省略了。

        情況一:同線程直接調(diào)用

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2????Object?lock?=?new?Object();
        3????syncLock(lock);
        4????Assertions.assertTrue(true);
        5}

        執(zhí)行結(jié)果為:

        1加鎖前?0x0000000000000001?(non-biasable;?age:?0)
        2加鎖中?0x0000700007830f10?(thin?lock:?0x0000700007830f10)
        3加鎖后?0x0000000000000001?(non-biasable;?age:?0)

        通過結(jié)果可以看到,加鎖前的對象頭是 0x0000000000000001,加鎖中是 0x0000700007830f10,加鎖后是 0x0000000000000001??粗赡懿惶靼?,這里簡單說下 64 位 jvm 的對象頭的分布情況

         1|--------------------------------------------------------------------------------------------------------------|
        2|??????????????????????????????????????????????Object?Header?(128?bits)????????????????????????????????????????|
        3|--------------------------------------------------------------------------------------------------------------|
        4|????????????????????????Mark?Word?(64?bits)????????????????????????????????????|??????Klass?Word?(64?bits)????|???????
        5|
        --------------------------------------------------------------------------------------------------------------|
        6|
        ??unused:25?|?identity_hashcode:31?|?unused:1?|?age:4?|?biased_lock:1?|?lock:2?|?????OOP?to?metadata?object???|??無鎖
        7|
        ----------------------------------------------------------------------|--------|------------------------------|
        8|
        ??thread:54?|?????????epoch:2??????|?unused:1?|?age:4?|?biased_lock:1?|?lock:2?|?????OOP?to?metadata?object???|??偏向鎖
        9|
        ----------------------------------------------------------------------|--------|------------------------------|
        10|
        ?????????????????????ptr_to_lock_record:62????????????????????????????|?lock:2?|?????OOP?to?metadata?object???|??輕量鎖
        11|
        ----------------------------------------------------------------------|--------|------------------------------|
        12|
        ?????????????????????ptr_to_heavyweight_monitor:62????????????????????|?lock:2?|?????OOP?to?metadata?object???|??重量鎖
        13|
        ----------------------------------------------------------------------|--------|------------------------------|
        14|
        ??????????????????????????????????????????????????????????????????????|?lock:2?|?????OOP?to?metadata?object???|??GC
        15|
        --------------------------------------------------------------------------------------------------------------|
        16

        lock: 鎖狀態(tài)標(biāo)記位,該標(biāo)記的值不同,整個mark word表示的含義不同。

        biased_lock:偏向鎖標(biāo)記,為1時表示對象啟用偏向鎖,為0時表示對象沒有偏向鎖。

        從分布可以得出,看鎖標(biāo)記,直接看后 3 位即可

        biased_locklock16進制狀態(tài)
        0011無鎖
        1015偏向
        0000輕量
        0102重量
        0113GC

        加鎖前的對象頭是 0x0000000000000001,加鎖中是 0x0000700007830f10,加鎖后是 0x0000000000000001

        從這種情況可以看出:加鎖前對象處于無鎖狀態(tài),加鎖中處于輕量鎖狀態(tài),釋放鎖后處于無鎖狀態(tài)

        這種現(xiàn)象和我們想象的可能不太一樣,在網(wǎng)上找了資料如下:

        JVM啟動時會進行一系列的復(fù)雜活動,比如裝載配置,系統(tǒng)類初始化等等。在這個過程中會使用大量 synchronized 關(guān)鍵字對對象加鎖,且這些鎖大多數(shù)都不是偏向鎖。為了減少初始化時間,JVM默認(rèn)延時加載偏向鎖。這個延時的時間大概為 4s 左右,具體時間因機器而異。當(dāng)然我們也可以設(shè)置 JVM 參數(shù) -XX:BiasedLockingStartupDelay=0 來取消延時加載偏向鎖。

        從上面可以看出,JVM默認(rèn)延時加載偏向鎖,時間大于 4s,為了更好的驗證,下面的代碼直接按 10s 處理。

        情況二:先獲取一次鎖,然后延遲 10s 再次獲取

        這個主要為了驗證一下上面的結(jié)論

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2????Object?lock?=?new?Object();
        3????syncLock(lock);
        4????TimeUnit.SECONDS.sleep(10);
        5????syncLock(lock);
        6????Assertions.assertTrue(true);
        7}

        日志輸出如下:

        1加鎖前?0x0000000000000001?(non-biasable;?age:?0)
        2加鎖中?0x00007000028aaf10?(thin?lock:?0x00007000028aaf10)
        3加鎖后?0x0000000000000001?(non-biasable;?age:?0)
        4
        5加鎖前?0x0000000000000001?(non-biasable;?age:?0)
        6加鎖中?0x00007000028aaf10?(thin?lock:?0x00007000028aaf10)
        7加鎖后?0x0000000000000001?(non-biasable;?age:?0)

        兩次獲取鎖都使用的輕量級鎖

        情況三:延遲 10s 后在創(chuàng)建鎖對象后調(diào)用

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2????TimeUnit.SECONDS.sleep(10);
        3????Object?lock?=?new?Object();
        4????syncLock(lock);
        5????Assertions.assertTrue(true);
        6}

        日志如下:

        1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中?0x00007fb114010805?(biased:?0x0000001fec450042;?epoch:?0;?age:?0)
        3加鎖后?0x00007fb114010805?(biased:?0x0000001fec450042;?epoch:?0;?age:?0)

        從這種情況可以看出:加鎖前對象處于偏向鎖狀態(tài),加鎖中處于偏向鎖狀態(tài),釋放鎖后處于偏向鎖狀態(tài),不過在加鎖前,并沒有偏向任何線程

        情況四:增加 BiasedLockingStartupDelay=0 參數(shù)

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2??Object?lock?=?new?Object();
        3??syncLock(lock);
        4??Assertions.assertTrue(true);
        5}

        日志

        1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中?0x00007fd650009005?(biased:?0x0000001ff5940024;?epoch:?0;?age:?0)
        3加鎖后?0x00007fd650009005?(biased:?0x0000001ff5940024;?epoch:?0;?age:?0)

        從這種情況可以看出:加鎖前對象處于偏向鎖狀態(tài),加鎖中處于偏向鎖狀態(tài),釋放鎖后處于偏向鎖狀態(tài),不過在加鎖前,并沒有偏向任何線程

        從上述四種情況可以得出:

        默認(rèn)情況 JVM 會延遲啟動偏向鎖功能,在 JVM 啟用偏向鎖功能前創(chuàng)建的鎖對象,直接使用輕量級鎖開始獲取鎖,而不會通過輕量級鎖階段。如果關(guān)閉延遲功能,可以使用 -XX:BiasedLockingStartupDelay=0 參數(shù)

        后面的測試情況使用 TimeUnit.SECONDS.sleep(10); 來實現(xiàn)和 -XX:BiasedLockingStartupDelay=0 的效果

        情況五:同線程多次調(diào)用

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2????TimeUnit.SECONDS.sleep(10);
        3????Object?lock?=?new?Object();
        4????syncLock(lock);
        5????syncLock(lock);
        6????Assertions.assertTrue(true);
        7}

        日志如下:

        1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中?0x0000023099602005?(biased:?0x000000008c265808;?epoch:?0;?age:?0)
        3加鎖后?0x0000023099602005?(biased:?0x000000008c265808;?epoch:?0;?age:?0)
        4
        5加鎖前?0x0000023099602005?(biased:?0x000000008c265808;?epoch:?0;?age:?0)
        6加鎖中??0x0000023099602005?(biased:?0x000000008c265808;?epoch:?0;?age:?0)
        7加鎖后?0x0000023099602005?(biased:?0x000000008c265808;?epoch:?0;?age:?0)

        從日志可以看出,第一次加鎖時,使用的偏向鎖,加鎖后偏向于 0x000000008c265808 第二次加鎖時,因為還在同一線程內(nèi),偏向鎖指向還是一樣,則直接獲取鎖,不進行鎖升級。

        情況六:多線程無競爭兩次調(diào)用

        1void?testSynchronizedLock()?throws?InterruptedException?{
        2????TimeUnit.SECONDS.sleep(10);
        3????Object?lock?=?new?Object();
        4????syncLock(lock);
        5????Thread?thread?=?new?Thread(()?->?syncLock(lock));
        6????thread.start();
        7????thread.join();
        8????Assertions.assertTrue(true);
        9}

        日志如下:

        1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中?0x00000264e4573005?(biased:?0x00000000993915cc;?epoch:?0;?age:?0)
        3加鎖后??0x00000264e4573005?(biased:?0x00000000993915cc;?epoch:?0;?age:?0)
        4
        5加鎖前?0x00000264e4573005?(biased:?0x00000000993915cc;?epoch:?0;?age:?0)
        6加鎖中?0x000000d0c6dff748?(thin?lock:?0x000000d0c6dff748)
        7加鎖后?0x0000000000000001?(non-biasable;?age:?0)

        從日志可以看出,第一次加鎖時,使用的偏向鎖,第二次加鎖時使用的輕量級鎖(8的二進制時 1000),從中可以得出,即使沒有競爭關(guān)系,只要有一個線程加過鎖,那另一個線程再加鎖就會變成輕量級鎖,從最后一次日志可以看出,最終又變成了無鎖狀態(tài)

        情況七:多線程無競爭很多次調(diào)用

         1void?testSynchronizedLock()?throws?InterruptedException?{
        2????TimeUnit.SECONDS.sleep(10);
        3????Object?lock?=?new?Object();
        4????syncLock(lock);
        5????Thread?thread?=?new?Thread(()?->?syncLock(lock));
        6????thread.start();
        7????thread.join();
        8????syncLock(lock);
        9????thread?=?new?Thread(()?->?syncLock(lock));
        10????thread.start();
        11????thread.join();
        12????Assertions.assertTrue(true);
        13}

        日志

         1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中??0x0000028212c72005?(biased:?0x00000000a084b1c8;?epoch:?0;?age:?0)
        3加鎖后?0x0000028212c72005?(biased:?0x00000000a084b1c8;?epoch:?0;?age:?0)
        4
        5加鎖前?0x0000028212c72005?(biased:?0x00000000a084b1c8;?epoch:?0;?age:?0)
        6加鎖中?0x000000ca803fefd8?(thin?lock:?0x000000ca803fefd8)
        7加鎖后?0x0000000000000001?(non-biasable;?age:?0)
        8
        9加鎖前?0x0000000000000001?(non-biasable;?age:?0)
        10加鎖中?0x000000cafeefc8a0?(thin?lock:?0x000000cafeefc8a0)
        11加鎖后?0x0000000000000001?(non-biasable;?age:?0)
        12
        13加鎖前?0x0000000000000001?(non-biasable;?age:?0)
        14加鎖中??0x000000ca803ff308?(thin?lock:?0x000000ca803ff308)
        15加鎖后?0x0000000000000001?(non-biasable;?age:?0)

        結(jié)果就是驗證了,輕量級鎖是可以轉(zhuǎn)換成無鎖的

        情況八:多線程有競爭調(diào)用

         1void?testSynchronizedLock()?throws?InterruptedException?{
        2????TimeUnit.SECONDS.sleep(10);
        3????Object?lock?=?new?Object();
        4????syncLock(lock);
        5????Thread?thread?=?new?Thread(()?->?syncLock(lock));
        6????Thread?thread2?=?new?Thread(()?->?syncLock(lock));
        7????thread.start();
        8????thread2.start();
        9
        10????thread.join();
        11????thread2.join();
        12????Assertions.assertTrue(true);
        13}
        14
        15void?syncLock(Object?lock)?{
        16????log.info("currentThread?{}",?Thread.currentThread().getId());
        17????log.info("加鎖前?{}",?ClassLayout.parseInstance(lock).toPrintable());
        18????synchronized?(lock)?{
        19????????try?{
        20????????????TimeUnit.SECONDS.sleep(10);
        21????????}?catch?(InterruptedException?e)?{
        22????????????e.printStackTrace();
        23????????}
        24????????log.info("加鎖中?{}",?ClassLayout.parseInstance(lock).toPrintable());
        25????}
        26????log.info("加鎖后?{}",?ClassLayout.parseInstance(lock).toPrintable());
        27}

        日志如下:

         1加鎖前?0x0000000000000005?(biasable;?age:?0)
        2加鎖中?0x000001b16e421005?(biased:?0x000000006c5b9084;?epoch:?0;?age:?0)
        3加鎖后??0x000001b16e421005?(biased:?0x000000006c5b9084;?epoch:?0;?age:?0)
        4
        5加鎖前?0x000001b16e421005?(biased:?0x000000006c5b9084;?epoch:?0;?age:?0)
        6加鎖前?0x000001b16e421005?(biased:?0x000000006c5b9084;?epoch:?0;?age:?0)
        7加鎖中?0x000001b10b4f0fba?(fat?lock:?0x000001b10b4f0fba)
        8加鎖后?0x000001b10b4f0fba?(fat?lock:?0x000001b10b4f0fba)
        9加鎖中?0x000001b10b4f0fba?(fat?lock:?0x000001b10b4f0fba)
        10加鎖后?0x000001b10b4f0fba?(fat?lock:?0x000001b10b4f0fba)

        從日志可以看出,顯示偏向,然后是重量級鎖,最后沒有變成無鎖



        瀏覽 83
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 四虎永久在线精品免费v | 最近中文字幕在线观看 | 欧美日韩国产一区二区不卡 | 国产在线网站 | 欧美色色色色色 |