激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java并發(二十)----synchronized原理進階

Java并發(二十)----synchronized原理進階

2024-01-03 01:01未知服務器之家 Java教程

1、小故事 故事角色 老王 - JVM 小南 - 線程 小女 - 線程 房間 - 對象 房間門上 - 防盜鎖 - Monitor-重量級鎖 房間門上 - 小南書包 - 輕量級鎖 房間門上 - 刻上小南大名 - 偏向鎖 -對象專屬于某個線程使用 批量重刻名 - 一個類的偏向鎖撤

1、小故事

故事角色

  • 老王 - JVM

  • 小南 - 線程

  • 小女 - 線程

  • 房間 - 對象

  • 房間門上 - 防盜鎖 - Monitor-重量級鎖

  • 房間門上 - 小南書包 - 輕量級鎖

  • 房間門上 - 刻上小南大名 - 偏向鎖 -對象專屬于某個線程使用

  • 批量重刻名 - 一個類的偏向鎖撤銷到達 20 閾值 -批量重偏向

  • 不能刻名字 - 批量撤銷該類對象的偏向鎖,設置該類不可偏向

小南要使用房間保證計算不被其它人干擾(原子性),最初,他用的是防盜鎖,當上下文切換時,鎖住門。這樣,即使他離開了,別人也進不了門,他的工作就是安全的。

但是,很多情況下沒人跟他來競爭房間的使用權。小女是要用房間,但使用的時間上是錯開的,小南白天用,小女晚上用。每次上鎖太麻煩了,有沒有更簡單的辦法呢?

小南和小女商量了一下,約定不鎖門了,而是誰用房間,誰把自己的書包掛在門口,但他們的書包樣式都一樣,因此每次進門前得翻翻書包,看課本是誰的,如果是自己的,那么就可以進門,這樣省的上鎖解鎖了。萬一書包不是自己的,那么就在門外等,并通知對方下次用鎖門的方式。

后來,小女回老家了,很長一段時間都不會用這個房間。小南每次還是掛書包,翻書包,雖然比鎖門省事了,但仍然覺得麻煩。

于是,小南干脆在門上刻上了自己的名字:【小南專屬房間,其它人勿用】,下次來用房間時,只要名字還在,那么說明沒人打擾,還是可以安全地使用房間。如果這期間有其它人要用這個房間,那么由使用者將小南刻的名字擦掉,升級為掛書包的方式。

同學們都放假回老家了,小南就膨脹了,在 20 個房間刻上了自己的名字,想進哪個進哪個。后來他自己放假回老家了,這時小女回來了(她也要用這些房間),結果就是得一個個地擦掉小南刻的名字,升級為掛書包的方式。老王覺得這成本有點高,提出了一種批量重刻名的方法,他讓小女不用掛書包了,可以直接在門上刻上自己的名字

后來,刻名的現象越來越頻繁,老王受不了了:算了,這些房間都不能刻名了,只能掛書包----設置該類不可偏向

節碼指令中有所體現

2、synchronized 原理進階

2.1 輕量級鎖

輕量級鎖的使用場景:如果一個對象雖然有多線程要加鎖,但加鎖的時間是錯開的(也就是沒有競爭),那么可以使用輕量級鎖來優化。

如果有競爭,輕量級鎖會升級為重量級鎖。

輕量級鎖對使用者是透明的,即語法仍然是 synchronized

假設有兩個方法同步塊,利用同一個對象加鎖

static final Object obj = new Object();
public static void method1() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊 A
 ? ? ? ?method2();
 ?  }
}
public static void method2() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊 B
 ?  }
}
  • 創建鎖記錄(Lock Record)對象,每個線程都的棧幀都會包含一個鎖記錄的結構,內部可以存儲鎖定對象的 Mark Word

Java并發(二十)----synchronized原理進階

  • 讓鎖記錄中 Object reference 指向鎖對象,并嘗試用 cas(compare and swap,原子性) 替換 Object 的 Mark Word,將 Mark Word 的值存入鎖記錄

Java并發(二十)----synchronized原理進階

  • 如果 cas(compare and swap,原子性) 替換成功,對象頭中存儲了鎖記錄地址和狀態 00,表示由該線程給對象加鎖,這時圖示如下

Java并發(二十)----synchronized原理進階

  • 如果 cas(compare and swap,原子性) 失敗,有兩種情況

    • 如果是其它線程已經持有了該 Object 的輕量級鎖,這時表明有競爭,進入鎖膨脹過程

    • 如果是自己執行了 synchronized 鎖重入,那么再添加一條 Lock Record 作為重入的計數,如示例代碼

Java并發(二十)----synchronized原理進階

  • 當退出 synchronized 代碼塊(解鎖時)如果有取值為 null 的鎖記錄,表示有重入,這時重置鎖記錄,表示重入計數減一(鎖重入的數量)

Java并發(二十)----synchronized原理進階

  • 當退出 synchronized 代碼塊(解鎖時)鎖記錄的值不為 null,這時使用 cas(compare and swap,原子性) 將 Mark Word 的值恢復給對象頭

    • 成功,則解鎖成功

    • 失敗,說明輕量級鎖進行了鎖膨脹或已經升級為重量級鎖,進入重量級鎖解鎖流程

2.2 鎖膨脹

如果在嘗試加輕量級鎖的過程中,CAS 操作無法成功,這時一種情況就是有其它線程為此對象加上了輕量級鎖(有競爭),這時需要進行鎖膨脹,將輕量級鎖變為重量級鎖。

static Object obj = new Object();
public static void method1() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊
 ?  }
}
  • 當 Thread-1 進行輕量級加鎖時,Thread-0 已經對該對象加了輕量級鎖

Java并發(二十)----synchronized原理進階

  • 這時 Thread-1 加輕量級鎖失敗,進入鎖膨脹流程

    • 即為 Object 對象申請 Monitor 鎖,讓 Object 指向重量級鎖地址

    • 然后自己進入 Monitor 的 EntryList BLOCKED

Java并發(二十)----synchronized原理進階

  • 當 Thread-0 退出同步塊解鎖時,使用 cas 將 Mark Word 的值恢復給對象頭,失敗。這時會進入重量級解鎖流程,即按照 Monitor 地址找到 Monitor 對象,設置 Owner 為 null,喚醒 EntryList 中 BLOCKED 線程

2.3 自旋優化

重量級鎖競爭的時候,還可以使用自旋來進行優化,如果當前線程自旋成功(即這時候持鎖線程已經退出了同步塊,釋放了鎖),這時當前線程就可以避免阻塞。

自旋重試成功的情況

線程 1 (core 1 上) 對象 Mark 線程 2 (core 2 上)
- 10(重量鎖) -
訪問同步塊,獲取 monitor 10(重量鎖)重量鎖指針 -
成功(加鎖) 10(重量鎖)重量鎖指針 -
執行同步塊 10(重量鎖)重量鎖指針 -
執行同步塊 10(重量鎖)重量鎖指針 訪問同步塊,獲取 monitor
執行同步塊 10(重量鎖)重量鎖指針 自旋重試
執行完畢 10(重量鎖)重量鎖指針 自旋重試
成功(解鎖) 01(無鎖) 自旋重試
- 10(重量鎖)重量鎖指針 成功(加鎖)
- 10(重量鎖)重量鎖指針 執行同步塊
- ... ...

線程2自旋重試3次成功加鎖,這樣就不會陷入阻塞。

自旋重試失敗的情況

線程 1(core 1 上) 對象 Mark 線程 2(core 2 上)
- 10(重量鎖) -
訪問同步塊,獲取 monitor 10(重量鎖)重量鎖指針 -
成功(加鎖) 10(重量鎖)重量鎖指針 -
執行同步塊 10(重量鎖)重量鎖指針 -
執行同步塊 10(重量鎖)重量鎖指針 訪問同步塊,獲取 monitor
執行同步塊 10(重量鎖)重量鎖指針 自旋重試
執行同步塊 10(重量鎖)重量鎖指針 自旋重試
執行同步塊 10(重量鎖)重量鎖指針 自旋重試
執行同步塊 10(重量鎖)重量鎖指針 阻塞
- ... ...
  • 線程2一直處于自旋,最后處于阻塞狀態。

  • 自旋會占用 CPU 時間,單核 CPU 自旋就是浪費,多核 CPU 自旋才能發揮優勢。

  • 在 Java 6 之后自旋鎖是自適應的,比如對象剛剛的一次自旋操作成功過,那么認為這次自旋成功的可能性會高,就多自旋幾次;反之,就少自旋甚至不自旋,總之,比較智能。

  • Java 7 之后不能控制是否開啟自旋功能

2.4 偏向鎖

輕量級鎖在沒有競爭時(就自己這個線程),每次鎖重入時仍然需要執行 CAS 操作。

Java 6 中引入了偏向鎖來做進一步優化:只有第一次使用 CAS 將線程 ID 設置到對象的 Mark Word 頭,之后發現這個線程 ID 是自己的就表示沒有競爭,不用重新 CAS。以后只要不發生競爭,這個對象就歸該線程所有

例如:

static final Object obj = new Object();
public static void m1() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊 A
 ? ? ? ?m2();
 ?  }
}
public static void m2() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊 B
 ? ? ? ?m3();
 ?  }
}
public static void m3() {
 ? ?synchronized( obj ) {
 ? ? ? ?// 同步塊 C
 ?  }
}
Java并發(二十)----synchronized原理進階

2.4.1 偏向狀態

回憶一下對象頭格式

|--------------------------------------------------------------------|--------------------|
| ? ? ? ? ? ? ? ? ? ? ? ?Mark Word (64 bits) ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? State ? ? ? ?|
|--------------------------------------------------------------------|--------------------|
| unused:25 | hashcode:31 | unused:1 | age:4 | biased_lock:0 | 01 ? ?| ? ? ? Normal ? ? ? | ?// 正常
|--------------------------------------------------------------------|--------------------| ?// 偏向鎖
| thread:54 | epoch:2 ? ? | unused:1 | age:4 | biased_lock:1 | 01 ? ?| ? ? ? Biased ? ? ? |
|--------------------------------------------------------------------|--------------------|
| ? ? ? ? ? ? ptr_to_lock_record:62 ? ? ? ? ? ? ? ? ? ? ? ? ?| 00 ? ?| Lightweight Locked | ?// 輕量級
|--------------------------------------------------------------------|--------------------| ?// 重量級
| ? ? ? ? ? ? ptr_to_heavyweight_monitor:62 ? ? ? ? ? ? ? ? ?| 10 ? ?| Heavyweight Locked |
|--------------------------------------------------------------------|--------------------|
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| 11 ? ?| ? ?Marked for GC ? |
|--------------------------------------------------------------------|--------------------|

一個對象創建時:

  • 如果開啟了偏向鎖(默認開啟),那么對象創建后,markword 值為 0x05 即最后 3 位為 101,這時它的 thread、epoch、age 都為 0

  • 偏向鎖是默認是延遲的,不會在程序啟動時立即生效,可以sleep 4s后查看,如果想避免延遲,可以加 VM 參數 -XX:BiasedLockingStartupDelay=0 來禁用延遲

    Java并發(二十)----synchronized原理進階

  • 如果沒有開啟偏向鎖,那么對象創建后,markword 值為 0x01 即最后 3 位為 001,這時它的 hashcode、age 都為 0,第一次用到 hashcode 時才會賦值

1) 測試延遲特性
2) 測試偏向鎖
class Dog {}

利用 jol 第三方工具來查看對象頭信息

pom文件

 ? ? ? ?<dependency>
 ? ? ? ? ? ?<groupId>org.openjdk.jol</groupId>
 ? ? ? ? ? ?<artifactId>jol-core</artifactId>
 ? ? ? ? ? ?<version>0.10</version>
 ? ? ? ?</dependency>

代碼(注意這一小節的代碼了解一下即可,在本機是運行不成功的,注意看輸出就行了

// 添加虛擬機參數 -XX:BiasedLockingStartupDelay=0 
public static void main(String[] args) throws IOException {
 ? ?Dog d = new Dog();
 ? ?ClassLayout classLayout = ClassLayout.parseInstance(d);
?
 ? ?new Thread(() -> {
 ? ? ? ?log.debug("synchronized 前");
 ? ? ? ?System.out.println(classLayout.toPrintableSimple(true));
 ? ? ? ?synchronized (d) {
 ? ? ? ? ? ?log.debug("synchronized 中");
 ? ? ? ? ? ?System.out.println(classLayout.toPrintableSimple(true));
 ? ? ?  }
 ? ? ? ?log.debug("synchronized 后");
 ? ? ? ?System.out.println(classLayout.toPrintableSimple(true));
 ?  }, "t1").start();
?
}

輸出

11:08:58.117 c.TestBiased [t1] - synchronized 前
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 ? ? ?  //VM 參數 `-XX:BiasedLockingStartupDelay=0`后
11:08:58.121 c.TestBiased [t1] - synchronized 中
00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 ? ? ? ? // 與上面輸出的區別,根據對象頭格式查看
11:08:58.121 c.TestBiased [t1] - synchronized 后
00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 ? ? ? ? // 處于偏向鎖,線程id不變,除非有新的競爭

注意

處于偏向鎖的對象解鎖后,線程 id 仍存儲于對象頭中

3)測試禁用

在上面測試代碼運行時在添加 VM 參數 -XX:-UseBiasedLocking 禁用偏向鎖

輸出

11:13:10.018 c.TestBiased [t1] - synchronized 前
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 ? ? // 三條日志后三位均不是101
11:13:10.021 c.TestBiased [t1] - synchronized 中
00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000 ? ? //  處于輕量級鎖
11:13:10.021 c.TestBiased [t1] - synchronized 后
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 ? ? // 恢復成正常狀態

4) 測試 hashCode,添加VM 參數 -XX:BiasedLockingStartupDelay=0 來禁用延遲

  • 正常狀態對象一開始是沒有 hashCode 的,第一次調用才生成

  • 調用 hashCode 會導致偏向鎖被禁用。因為處于偏向鎖狀態的話已經存儲線程id,再去存儲hashcode,空間不夠,是存儲不下的。所以這時候的狀態會被改為正常狀態。另外輕量級鎖的hashcode存儲在棧幀中的鎖記錄中,重量級鎖的hashcode存儲在monitor對象中,解鎖時會還原。

2.4.2 撤銷 - 調用對象 hashCode

調用了對象的 hashCode,但偏向鎖的對象 MarkWord 中存儲的是線程 id,如果調用 hashCode 會導致偏向鎖被撤銷

  • 輕量級鎖會在鎖記錄中記錄 hashCode

  • 重量級鎖會在 Monitor 中記錄 hashCode

在調用 hashCode 后使用偏向鎖,記得去掉 -XX:-UseBiasedLocking

輸出

11:22:10.386 c.TestBiased [main] - 調用 hashCode:1778535015
11:22:10.391 c.TestBiased [t1] - synchronized 前
00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001
11:22:10.393 c.TestBiased [t1] - synchronized 中
00000000 00000000 00000000 00000000 00100000 11000011 11110011 01101000
11:22:10.393 c.TestBiased [t1] - synchronized 后
00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001

2.4.3 撤銷 - 其它線程使用對象

當有其它線程使用偏向鎖對象時,會將偏向鎖升級為輕量級鎖

private static void test2() throws InterruptedException {
?
 ? ?Dog d = new Dog();
 ? ?Thread t1 = new Thread(() -> {
 ? ? ? ?synchronized (d) {
 ? ? ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ? ? ? ?// 鎖住當前類對象
 ? ? ? ?synchronized (TestBiased.class) {
 ? ? ? ? ? ?TestBiased.class.notify();
 ? ? ?  }
 ? ? ? ?// 如果不用 wait/notify 使用 join 必須打開下面的注釋
 ? ? ? ?// 因為:t1 線程不能結束,否則底層線程可能被 jvm 重用作為 t2 線程,底層線程 id 是一樣的
 ? ? ? ?/*try {
 ? ? ? ? ? ?System.in.read();
 ? ? ? ?} catch (IOException e) {
 ? ? ? ? ? ?e.printStackTrace();
 ? ? ? ?}*/
 ?  }, "t1");
 ? ?t1.start();
?
?
 ? ?Thread t2 = new Thread(() -> {
 ? ? ? ?// 鎖住當前類對象
 ? ? ? ?synchronized (TestBiased.class) {
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?TestBiased.class.wait(); // 等待t1線程
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?e.printStackTrace();
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ?synchronized (d) {
 ? ? ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ?  }, "t2");
 ? ?t2.start();
}

輸出

[t1] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101  // 處于偏向鎖    
[t2] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101  // t2線程還未加鎖和t1狀態保持一致
[t2] - 00000000 00000000 00000000 00000000 00011111 10110101 11110000 01000000  //  處于輕量級鎖
[t2] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001  //  解鎖后處于不可偏向

2.4.4 撤銷 - 調用 wait/notify

這種情況下也會撤銷偏向鎖。因為wait/notify只有重量級鎖才有。會將偏向鎖或者輕量級鎖升級為重量級鎖

public static void main(String[] args) throws InterruptedException {
 ? ?Dog d = new Dog();
?
 ? ?Thread t1 = new Thread(() -> {
 ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ?synchronized (d) {
 ? ? ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?d.wait();
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?e.printStackTrace();
 ? ? ? ? ?  }
 ? ? ? ? ? ?log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ?  }, "t1");
 ? ?t1.start();
?
 ? ?new Thread(() -> {
 ? ? ? ?try {
 ? ? ? ? ? ?Thread.sleep(6000);
 ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ?e.printStackTrace();
 ? ? ?  }
 ? ? ? ?synchronized (d) {
 ? ? ? ? ? ?log.debug("notify");
 ? ? ? ? ? ?d.notify();
 ? ? ?  }
 ?  }, "t2").start();
?
}

輸出

[t1] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101  // 偏向鎖
[t1] - 00000000 00000000 00000000 00000000 00011111 10110011 11111000 00000101  // 加鎖
[t2] - notify
[t1] - 00000000 00000000 00000000 00000000 00011100 11010100 00001101 11001010  // 重量級鎖

2.4.5 批量重偏向

如果對象雖然被多個線程訪問,但沒有競爭,這時偏向了線程 T1 的對象仍有機會重新偏向 T2,重偏向會重置對象的 Thread ID

當撤銷偏向鎖閾值超過 20 次后,jvm 會這樣覺得,我是不是偏向錯了呢,于是會在給這些對象加鎖時重新偏向至加鎖線程

private static void test3() throws InterruptedException {
?
 ? ?Vector<Dog> list = new Vector<>();
 ? ?Thread t1 = new Thread(() -> {
 ? ? ? ?for (int i = 0; i < 30; i++) {
 ? ? ? ? ? ?Dog d = new Dog();
 ? ? ? ? ? ?list.add(d);
 ? ? ? ? ? ?synchronized (d) {
 ? ? ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?synchronized (list) {
 ? ? ? ? ? ?list.notify();
 ? ? ?  } ? ? ? ?
 ?  }, "t1");
 ? ?t1.start();
?
 ? ?
 ? ?Thread t2 = new Thread(() -> {
 ? ? ? ?synchronized (list) {
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?list.wait();
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?e.printStackTrace();
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?log.debug("===============> ");
 ? ? ? ?for (int i = 0; i < 30; i++) {
 ? ? ? ? ? ?Dog d = list.get(i);
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ? ?synchronized (d) {
 ? ? ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ?  }
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ?  }, "t2");
 ? ?t2.start();
}

輸出

[t1] - 0    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101  // 偏向鎖
[t1] - 1    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 2    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 3    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 4    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 5    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 6    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 7    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 8    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 9    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 10   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 11   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 12   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 13   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 14   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 15   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 16   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 17   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 18   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 19   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 20   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 21   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 22   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 23   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 24   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 25   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 26   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 27   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 28   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 29   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - ===============> 
[t2] - 0    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 // 偏向t1鎖
[t2] - 0    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000  // 撤銷偏向鎖,升級為輕量級鎖
[t2] - 0    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001  // 處于不可偏向鎖,也就是正常狀態
[t2] - 1    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 1    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 1    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 2    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 2    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 2    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 3    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 3    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 3    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 4    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 4    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 4    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 5    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 5    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 5    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 6    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 6    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 6    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 7    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 7    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 7    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 8    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 8    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 8    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 9    00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 9    00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 9    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 10   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 10   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 10   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 11   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 11   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 11   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 12   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 12   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 12   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 13   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 13   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 13   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 14   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 14   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 14   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 15   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 15   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 15   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 16   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 16   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 16   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 17   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 17   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 17   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 18   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 18   00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 18   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 19   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101  // 第20個對象開始,全部處于偏向t2的偏向鎖
[t2] - 19   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101  // 批量重偏向,后面所有都處于偏向t2的偏向鎖
[t2] - 19   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 20   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 21   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 22   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 23   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 24   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 25   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 26   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 27   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 28   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29   00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 29   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29   00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101

2.4.6 批量撤銷

當撤銷偏向鎖閾值超過 40 次后,jvm 會這樣覺得,自己確實偏向錯了,根本就不該偏向。于是整個類的所有對象都會變為不可偏向的,新建的對象也是不可偏向的

這里就不打印日志了,可以自行思考

static Thread t1,t2,t3;
private static void test4() throws InterruptedException {
 ? ?Vector<Dog> list = new Vector<>();
?
 ? ?int loopNumber = 39;
 ? ?t1 = new Thread(() -> {
 ? ? ? ?for (int i = 0; i < loopNumber; i++) {
 ? ? ? ? ? ?Dog d = new Dog();
 ? ? ? ? ? ?list.add(d);
 ? ? ? ? ? ?synchronized (d) {
 ? ? ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?LockSupport.unpark(t2);
 ?  }, "t1");
 ? ?t1.start();
?
 ? ?t2 = new Thread(() -> {
 ? ? ? ?LockSupport.park();
 ? ? ? ?log.debug("===============> ");
 ? ? ? ?for (int i = 0; i < loopNumber; i++) {
 ? ? ? ? ? ?Dog d = list.get(i);
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ? ?synchronized (d) {
 ? ? ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ?  }
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ? ? ? ?LockSupport.unpark(t3);
 ?  }, "t2");
 ? ?t2.start();
?
 ? ?t3 = new Thread(() -> {
 ? ? ? ?LockSupport.park();
 ? ? ? ?log.debug("===============> ");
 ? ? ? ?for (int i = 0; i < loopNumber; i++) {
 ? ? ? ? ? ?Dog d = list.get(i);
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ? ?synchronized (d) {
 ? ? ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ? ? ?  }
 ? ? ? ? ? ?log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
 ? ? ?  }
 ?  }, "t3");
 ? ?t3.start();
?
 ? ?t3.join();
 ? ?log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
}

參考資料

https://github.com/farmerjohngit/myblog/issues/12

https://www.cnblogs.com/LemonFive/p/11246086.html

https://www.cnblogs.com/LemonFive/p/11248248.html

偏向鎖論文

2.5 鎖消除

鎖消除

@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations=3)
@Measurement(iterations=5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
 ? ?static int x = 0;
 ? ?@Benchmark
 ? ?public void a() throws Exception {
 ? ? ? ?x++;
 ?  }
 ? ?@Benchmark
 ? ?public void b() throws Exception {
 ? ? ? ?Object o = new Object();
 ? ? ? ?synchronized (o) {
 ? ? ? ? ? ?x++;
 ? ? ?  }
 ?  }
}

java -jar benchmarks.jar

Benchmark ? ? ? ? ?  Mode  Samples  Score  Score error  Units
c.i.MyBenchmark.a ?  avgt ? ? ?  5  1.542 ? ? ?  0.056  ns/op
c.i.MyBenchmark.b ?  avgt ? ? ?  5  1.518 ? ? ?  0.091  ns/op

b是有一個加鎖的操作,那為什么a耗時與b耗時幾乎別區別呢?是因為Java中有一個JIT(即時編譯器),會對于反復執行的代碼進行優化,b中o對象根本不會被共享,所以b中的synchronized是沒有任何意義的,所以Java就把鎖給消除了。這也是存在一個開關的,這個開關是默認開啟的,下面演示下把這個開關關閉掉。

java -XX:-EliminateLocks -jar benchmarks.jar

Benchmark ? ? ? ? ?  Mode  Samples ? Score  Score error  Units
c.i.MyBenchmark.a ?  avgt ? ? ?  5 ? 1.507 ? ? ?  0.108  ns/op
c.i.MyBenchmark.b ?  avgt ? ? ?  5  16.976 ? ? ?  1.572  ns/op

可以看到耗時有明顯差距了。

鎖粗化

對相同對象多次加鎖,導致線程發生多次重入,可以使用鎖粗化方式來優化,這不同于之前的細分鎖的粒度。

?

?

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91久久一区| 久久成人精品视频 | 羞羞视频免费网站 | 欧美综合日韩 | 久久毛片 | 欧美毛片 | 日比视频| 青青草免费观看完整版高清 | 2017亚洲男人天堂 | 成人免费看毛片 | 日韩电影一区二区 | 国产精品一区网站 | 一级性生活免费视频 | 91女上位 在线播放 性欧美日本 | 电视剧全部免费观看 | 久久人人av| 最新在线黄色网址 | 欧美18videos性处按摩 | 国产91在线免费 | 久久精品a一级国产免视看成人 | 成人在线免费视频播放 | 成人久久久精品乱码一区二区三区 | 男人的天堂色偷偷 | 天天看逼 | www日韩大片 | 国产一区二区欧美精品 | 真人一级毛片免费 | 久久久噜噜噜久久熟有声小说 | 日本精品久久久久久草草 | 免费大香伊蕉在人线国产 | 国产一区网址 | 欧美a级大胆视频 | 91a在线观看 | 999精品久久久| 久久精品中文 | 色妞色视频一区二区三区四区 | 手机av免费电影 | 亚洲精品成人在线视频 | 精品久久久久久国产三级 | 青草久久网 | 欧美一区二区三区免费观看 |