鎖的驗證- 無鎖->偏向鎖->輕量級鎖->重量級鎖->無鎖過程
一、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
二、無鎖狀態
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)多個線程存在競爭時重量級鎖。
很多時候,偏向鎖->輕量級鎖->重量級鎖的轉化過程,基本你只能看見開頭和結尾,因爲輕量級鎖的自旋被優化不會長時間持續進行的,所以你看到的就是偏向鎖到重量級鎖的過程。