synchronized鎖的驗證 [無鎖-」偏向鎖-」輕量級鎖-」重量級鎖-」無鎖過程]

鎖的驗證- 無鎖->偏向鎖->輕量級鎖->重量級鎖->無鎖過程

一、JVM 設置參數

# 開啓偏向鎖
-XX:+UseBiasedLocking
# 關閉偏向鎖延遲
-XX:BiasedLockingStartupDelay=0
# 查看所有的 JVM 參數
-XX:+PrintFlagsFinal
# 設置重偏向閾值
-XX:BiasedLockingBulkRebiasThreshold=20
# 批量重偏向距離上次批量重偏向的後重置的延遲時間
-XX:BiasedLockingDecayTime=25000
# 設置批量撤銷閾值
-XX:BiasedLockingBulkRevokeThreshold=40

在 JVM 啓動的時候會有很多線程在後臺運行,例如 GC 線程,Finalizer 線程,VM Thread 線程等,會用到很多同步操作,所以在啓動的前 4 秒默認創建的對象都不支持偏向,因爲有默認參數

	-XX:BiasedLockingStartupDelay=4000

二、無鎖狀態

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-V4hwbHgh-1594019771902)(/Users/haocongcong/Library/Containers/com.tencent.WeWorkMac/Data/Library/Application Support/WXWork/Data/1688850015369412/Cache/Image/2020-06/無鎖二進制.png)]

     public void test1(){
	     Object model2 = new Object();
        System.out.println(ClassLayout.parseInstance(model2).toPrintable());
        System.out.println("hash: -----" + model2.hashCode());
        // 偏向鎖
        System.out.println(ClassLayout.parseInstance(model2).toPrintable());
        binaryToDecimal(model2.hashCode());   
     }
OFFSET  SIZE   TYPE DESCRIPTION            VALUE
      0     4        (object header)       01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)       00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)       e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

hash: -----1975358023
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION           VALUE
      0     4        (object header)       01 47 92 bd (00000001 01000111 10010010 10111101) (-1114487039)
      4     4        (object header)       75 00 00 00 (01110101 00000000 00000000 00000000) (117)
      8     4        (object header)       e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

header中前8個字節按照平時習慣的從高位到低位的展示爲:

00000000 00000000 00000000 01110101 10111101 10010010 01000111 00000001

對照上圖。最後3位001,代表無鎖狀態
中間31位(01110101 10111101 10010010 01000111)換算成10進制,即爲上圖打印的hash值:1975358023

偏向鎖

    public static void main(String[] args) throws InterruptedException {
         //JVM 啓動 5 秒後創建對象
        Thread.sleep(5000);
        Object object = new Object();
        System.out.println(ClassLayout.parseInstance(object).toPrintable());
        //偏向鎖
        synchronized (object){
            System.out.println(ClassLayout.parseInstance(object).toPrintable());
        }

    }
//匿名偏向 
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION         VALUE
      0     4        (object header)     05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)     00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)     e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


//偏向鎖
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION        VALUE
      0     4        (object header)    05 68 01 b9 (00000101 01101000 00000001 10111001) (-1191090171)
      4     4        (object header)    f7 7f 00 00 (11110111 01111111 00000000 00000000) (32759)
      8     4        (object header)    e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)

偏向鎖和hashCode方法

當一個對象已經計算過identity hash code,它就無法進入偏向鎖狀態;當一個對象當前正處於偏向鎖狀態,並且需要計算其identity hash code的話,則它的偏向鎖會被撤銷。
那什麼時候對象會計算identity hash code呢?當然是當你調用未覆蓋的Object.hashCode()方法或者System.identityHashCode(Object o)時候了。

 public static void main(String[] args) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Object model2 = new Object();
        System.out.println(ClassLayout.parseInstance(model2).toPrintable());
        System.out.println("hash: -----" + model2.hashCode());
//        System.out.println("hashcode:: "+Integer.toBinaryString(model2.hashCode()));
        System.out.println("----------after hashcode----------");
        System.out.println(ClassLayout.parseInstance(model2).toPrintable());
        synchronized (model2){
            System.out.println("---------after lock-------");
            model2.hashCode();
            System.out.println(ClassLayout.parseInstance(model2).toPrintable());
        }

    }
    }
//偏向鎖
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

hash: -----1911728085
----------after hashcode----------
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 d5 a7 f2 (00000001 11010101 10100111 11110010) (-223881983)
      4     4        (object header)                           71 00 00 00 (01110001 00000000 00000000 00000000) (113)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

---------after lock-------
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           d0 a8 4c 04 (11010000 10101000 01001100 00000100) (72132816)
      4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

輕量級鎖

	public static void main(String[] args) throws InterruptedException {
         //JVM 啓動 5 秒後創建對象
        Thread.sleep(5000);
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 進入偏向鎖狀態");
            synchronized (object) {
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        });
        t1.start();
        t1.join();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + " 進入輕量級鎖狀態");
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        }).start();

    }
	Thread-0 進入偏向鎖狀態
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION         VALUE
      0     4        (object header)     05 e8 18 a8 (00000101 11101000 00011000 10101000) (-1474762747)
      4     4        (object header)     a4 7f 00 00 (10100100 01111111 00000000 00000000) (32676)
      8     4        (object header)     e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

Thread-2 進入輕量級鎖狀態
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION        VALUE
      0     4        (object header)    68 c8 0c 06 (01101000 11001000 00001100 00000110) (101501032)
      4     4        (object header)    00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4        (object header)    e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)

#重量級鎖

public static void main(String[] args) throws InterruptedException {
        //JVM 啓動 5 秒後創建對象
        Thread.sleep(5000);
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 進入偏向鎖狀態");
            synchronized (object) {

                System.out.println(LocalDateTime.now()+",t1鎖狀態:"+ClassLayout.parseInstance(object).toPrintable());
            }
        });
        t1.setName("t1");
        t1.start();
        t1.join();

        Thread t2 = new Thread(() -> {
            synchronized (object) {
                System.out.println("t2:"+ClassLayout.parseInstance(object).toPrintable());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2鎖狀態:"+ClassLayout.parseInstance(object).toPrintable());
            }
        });
        t2.setName("t2");
        t2.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread t3 = new Thread(() -> {

            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + " 拿到鎖,狀態:");
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
        });
        t3.setName("t3");
        t3.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("after all:"+ClassLayout.parseInstance(object).toPrintable());
    }

輸出結果

無鎖->偏向鎖->輕量級鎖->重量級鎖->無鎖

t1鎖狀態:java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 40 99 29 (00000101 01000000 10011001 00101001) (697909253)
      4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t2:java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 40 99 29 (00000101 01000000 10011001 00101001) (697909253)
      4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t2鎖狀態:java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           4a 97 81 2a (01001010 10010111 10000001 00101010) (713135946)
      4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

t3 拿到鎖,狀態:
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           4a 97 81 2a (01001010 10010111 10000001 00101010) (713135946)
      4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

after all:java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

總結一下,其實無鎖->偏向鎖->輕量級鎖->重量級鎖的轉化過程中沒那麼複雜,注意記住:
(1)只有一個線程獲取鎖時,就是偏向鎖。
(2)多個線程時,不存在競爭(多個線程順序執行),輕量級鎖。
(3)多個線程存在競爭時重量級鎖。
很多時候,偏向鎖->輕量級鎖->重量級鎖的轉化過程,基本你只能看見開頭和結尾,因爲輕量級鎖的自旋被優化不會長時間持續進行的,所以你看到的就是偏向鎖到重量級鎖的過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章