<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java 多線程共享模型之管程(上)

      主線程與守護線程

      默認情況下,Java 進程需要等待所有線程都運行結束,才會結束。有一種特殊的線程叫做守護線程,只要其它非守護線程運行結束了,即使守護線程的代碼沒有執行完,也會強制結束。

      package Daemon;
      
      import lombok.extern.slf4j.Slf4j;
      
      @Slf4j(topic = "c.demo1")
      public class demo1 {
          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> {
                  while (true) {
                      if (Thread.currentThread().isInterrupted()) {
                          break;
                      }
                  }
                  log.debug("結束");
              }, "t1");
              t1.setDaemon(true);
              t1.start();
      
              Thread.sleep(1000);
              log.debug("結束");
          }
      }
      

      輸出:

      15:08:26 [main] c.demo1 - 結束
      
      Process finished with exit code 0
      

      注意

      • 垃圾回收器線程就是一種守護線程
      • Tomcat 中的 Acceptor 和 Poller 線程都是守護線程,所以 Tomcat 接收到 shutdown 命令后,不會等待它們處理完當前請求

      五種狀態

      這是從 操作系統 層面來描述的

      • 【初始狀態】僅是在語言層面創建了線程對象,還未與操作系統線程關聯
      • 【可運行狀態】(就緒狀態)指該線程已經被創建(與操作系統線程關聯),可以由 CPU 調度執行
      • 【運行狀態】指獲取了 CPU 時間片運行中的狀態
        • 當 CPU 時間片用完,會從【運行狀態】轉換至【可運行狀態】,會導致線程的上下文切換
      • 【阻塞狀態】
        • 如果調用了阻塞 API,如 BIO 讀寫文件,這時該線程實際不會用到 CPU,會導致線程上下文切換,進入【阻塞狀態】
        • 等 BIO 操作完畢,會由操作系統喚醒阻塞的線程,轉換至【可運行狀態】
        • 與【可運行狀態】的區別是,對【阻塞狀態】的線程來說只要它們一直不喚醒,調度器就一直不會考慮調度它們
      • 【終止狀態】表示線程已經執行完畢,生命周期已經結束,不會再轉換為其它狀態

      六種狀態

      這是從 Java API 層面來描述的

      • NEW 線程剛被創建,但是還沒有調用 start() 方法
      • RUNNABLE 當調用了 start() 方法之后,注意,Java API 層面的 RUNNABLE 狀態涵蓋了 操作系統 層面的【可運行狀態】、【運行狀態】和【阻塞狀態】(由于 BIO 導致的線程阻塞,在 Java 里無法區分,仍然認為是可運行)
      • BLOCKED , WAITING , TIMED_WAITING 都是 Java API 層面對【阻塞狀態】的細分
      • TERMINATED 當線程代碼運行結束

      共享模型之管程

      共享帶來的問題

      兩個線程對初始值為 0 的靜態變量一個做自增,一個做自減,各做 5000 次,結果是 0 嗎?

      package gc;
      
      import lombok.extern.slf4j.Slf4j;
      
      @Slf4j(topic = "c.demo1")
      public class demo1 {
      
          static int counter = 0;
      
          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      counter++;
                  }
              },"t1");
      
              Thread t2 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      counter--;
                  }
              },"t2");
      
              t1.start();
              t2.start();
              t1.join();
              t2.join();
              log.debug("{}",counter);
          }
      }
      

      輸出:

      16:03:58 [main] c.demo1 - -1238
      

      問題分析

      以上的結果可能是正數、負數、零。為什么呢?因為 Java 中對靜態變量的自增,自減并不是原子操作,要徹底理解,必須從字節碼來進行分析

      例如對于 i++ 而言(i 為靜態變量),實際會產生如下的 JVM 字節碼指令:

      getstatic i // 獲取靜態變量i的值
      iconst_1 // 準備常量1
      iadd // 自增
      putstatic i // 將修改后的值存入靜態變量i
      

      而 Java 的內存模型如下,完成靜態變量的自增,自減需要在主存和工作內存中進行數據交換:

      臨界區

      • 一個程序運行多個線程本身是沒有問題的
      • 問題出在多個線程訪問共享資源
        • 多個線程讀共享資源其實也沒有問題
        • 在多個線程對共享資源讀寫操作時發生指令交錯,就會出現問題
      • 一段代碼塊內如果存在對共享資源的多線程讀寫操作,稱這段代碼塊為臨界區

      競態條件

      多個線程在臨界區內執行,由于代碼的執行序列不同而導致結果無法預測,稱之為發生了競態條件

      synchronized 解決方案

      使用阻塞式的解決方案:synchronized,來解決上述問題,即俗稱的【對象鎖】,它采用互斥的方式讓同一時刻至多只有一個線程能持有【對象鎖】,其它線程再想獲取這個【對象鎖】時就會阻塞住。這樣就能保證擁有鎖的線程可以安全的執行臨界區內的代碼,不用擔心線程上下文切換

      注意

      雖然 java 中互斥和同步都可以采用 synchronized 關鍵字來完成,但它們還是有區別的:

      • 互斥是保證臨界區的競態條件發生,同一時刻只能有一個線程執行臨界區代碼
      • 同步是由于線程執行的先后、順序不同、需要一個線程等待其它線程運行到某個點
      package gc;
      
      import lombok.extern.slf4j.Slf4j;
      
      @Slf4j(topic = "c.demo1")
      public class demo1 {
      
          static int counter = 0;
          static final Object lock = new Object();
      
          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      synchronized (lock){
                          counter++;
                      }
                  }
              },"t1");
      
              Thread t2 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      synchronized (lock){
                          counter--;
                      }
                  }
              },"t2");
      
              t1.start();
              t2.start();
              t1.join();
              t2.join();
              log.debug("{}",counter);
          }
      }
      

      輸出:

      16:14:15 [main] c.demo1 - 0
      

      改進:由面向過程改為面向對象

      package gc;
      
      import lombok.extern.slf4j.Slf4j;
      
      @Slf4j(topic = "c.demo1")
      public class demo1 {
          public static void main(String[] args) throws InterruptedException {
              Room room = new Room();
              Thread t1 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      room.increment();
                  }
              },"t1");
      
              Thread t2 = new Thread(() -> {
                  for(int i=0;i<5000;i++){
                      room.decrement();
                  }
              },"t2");
      
              t1.start();
              t2.start();
              t1.join();
              t2.join();
              log.debug("{}",room.getCounter());
          }
      }
      
      class Room{
          private int counter = 0;
      
          public void increment(){
              synchronized (this){
                  counter++;
              }
          }
      
          public void decrement(){
              synchronized (this){
                  counter--;
              }
          }
      
          public int getCounter(){
              synchronized (this){
                  return counter;
              }
          }
      }
      

      輸出:

      16:18:22 [main] c.demo1 - 0
      

      方法上的 synchronized

      • 加在成員方法上,鎖住的是 this
      • 加在靜態方法上,鎖住的是 類名.class
      class Test{
       	public synchronized void test() {
       
       	}
      }
      
      等價于
      class Test{
       	public void test() {
       		synchronized(this) {
       
       		}
       	}
      }
      
      class Test{
       	public synchronized static void test() {
       	
       	}
      }
      
      等價于
      class Test{
       	public static void test() {
       		synchronized(Test.class) {
       
       		}
       	}
      }
      

      變量的線程安全分析

      成員變量和靜態方法是否線程安全

      • 如果它們沒有共享,則線程安全
      • 如果它們被共享了,根據它們的狀態是否能夠改變,又分兩種情況
        • 如果只有讀操作,則線程安全
        • 如果有讀寫操作,則這段代碼是臨界區,需要考慮線程安全

      局部變量是否線程安全

      public static void test1() {
       	int i = 10;
       	i++; 
      }
      

      每個線程調用 test1() 方法時局部變量 i,會在每個線程的棧幀內存中被創建多份,因此不存在共享,所以局部變量是線程安全的

      常見的線程安全類

      • String
      • Integer
      • StringBuffffer
      • Random
      • Vector
      • Hashtable
      • java.util.concurrent 包下的類

      這里說它們是線程安全的是指,多個線程調用它們同一個實例的某個方法時,是線程安全的。也可以理解為

      Hashtable table = new Hashtable();
      
      new Thread(()->{
       	table.put("key", "value1");
      }).start();
      
      new Thread(()->{
       	table.put("key", "value2");
      }).start();
      
      • 它們的每個方法是原子的
      • 注意它們多個方法的組合不是原子的

      不可變類線程安全性

      String、Integer 等都是不可變類,因為其內部的狀態不可以改變,因此它們的方法都是線程安全的

      Monitor

      Java 對象頭

      (以 32 位虛擬機為例)

      • 普通對象

        Klass Word:是一個指針,指向該對象從屬的 class 類

      • 數組對象

      其中 Mark Word 結構為:

      • hashcode:每個對象的哈希碼
      • age:垃圾回收時用到的分代年齡
      • biased_lock:是否是偏向鎖
      • (01,00,10,11):表示加鎖狀態

      Monitor(鎖)

      過程:

      1. 首先用一個指針試圖將 obj 對象與操作系統中的 Monitor 對象關聯。在正常狀態下,Mark Word 存儲了 hashcode,age等信息,并且加鎖狀態碼為 “01” 表示并未與任何鎖關聯。但是一旦獲得了鎖,加鎖狀態碼會改為 “10” 并且拋棄掉存儲的 hashcode ,age 等信息,轉而存儲一個指向 Monitor 對象的指針(ptr_to_heavyweight_monitor),占30位

      2. 此時線程 Thread-2 指向 Monitor 中的 Owner 表示自己是這把鎖現在的主人

      3. 當一個新的線程到來時(Thread-1),會先去檢查此對象有沒有關聯 Monitor 對象,發現已經關聯,繼而檢查 Monitor 對象中的 Owner 已經是 Thread-2 了,此時 Thread-1會跟Monitor 中的 EntryList(阻塞隊列) 關聯,進入 BLOCK 狀態

      4. Thread-2 執行完同步代碼塊的內容,然后喚醒 EntryList 中等待的線程來競爭鎖,競爭的時是非公平的

      synchronized 優化原理

      輕量級鎖

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

      輕量級鎖對使用者是透明的,即語法仍然是 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

      • 讓鎖記錄中 Object reference 指向鎖對象,并嘗試用 cas 替換 Object 的 Mark Word,將 Mark Word 的值存入鎖記錄

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

      • 如果 cas 失敗,有兩種情況

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

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

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

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

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

      鎖膨脹

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

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

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

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

        • 然后自己進入 Monitor 的 EntryList BLOCKED

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

      自旋優化

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

      • 自旋會占用 CPU 時間,單核 CPU 自旋就是浪費,多核 CPU 自旋才能發揮優勢。
      • 在 Java 6 之后自旋鎖是自適應的,比如對象剛剛的一次自旋操作成功過,那么認為這次自旋成功的可能性會高,就多自旋幾次;反之,就少自旋甚至不自旋,總之,比較智能。
      • Java 7 之后不能控制是否開啟自旋功能

      偏向鎖

      輕量級鎖在沒有競爭時(就自己這個線程),每次重入仍然需要執行 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
       	}
      }
      
      偏向狀態

      一個對象創建時:

      • 如果開啟了偏向鎖(默認開啟),那么對象創建后,markword 值為 0x05 即最后 3 位為 101,這時它的thread、epoch、age 都為 0
      • 偏向鎖是默認是延遲的,不會在程序啟動時立即生效,如果想避免延遲,可以加 VM 參數 -XX:BiasedLockingStartupDelay=0 來禁用延遲
      • 如果沒有開啟偏向鎖,那么對象創建后,markword 值為 0x01 即最后 3 位為 001,這時它的 hashcode、age 都為 0,第一次用到 hashcode 時才會賦值

      1) 測試延遲特性

      2) 測試偏向鎖

      class Dog {}
      

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

      // 添加虛擬機參數 -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 
      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
      

      3)測試禁用

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

      輸出:

      11:13:10.018 c.TestBiased [t1] - synchronized 前
      00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
      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

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

      撤銷 - 調用對象 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
      
      撤銷 - 其他線程使用對象

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

      	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();
                      } 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] - 00000000 00000000 00000000 00000000 00011111 10110101 11110000 01000000 
      [t2] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
      
      撤銷 - 調用 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
      
      批量重偏向

      如果對象雖然被多個線程訪問,但沒有競爭,這時偏向了線程 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 
      [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 
      [t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
      [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
      
      批量撤銷

      當撤銷偏向鎖閾值超過 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));
          }
      
      posted @ 2022-06-06 19:27  染沁  閱讀(291)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产自拍一区二区三区在线| 四虎在线成人免费观看| 国产欧美精品一区二区三区-老狼 真实单亲乱l仑对白视频 | 久久一日本道色综合久久| 自拍偷拍第一区二区三区| 日本亚洲色大成网站www久久| 亚洲国产区男人本色vr| 国产免费播放一区二区三区| 亚洲国产日韩一区三区| 国产一区二区三区色老头| 精品久久久久国产免费| 久久人人爽人人爽人人av| 日本丰满人妻xxxxxhd| 视频一区视频二区在线视频| 91色老久久精品偷偷蜜臀| 这里只有精品在线播放| 精品人妻码一区二区三区| 亚洲乱码一二三四区国产| 国产性三级高清在线观看| 久热这里只有精品12| 国内视频偷拍久久伊人网| 福利视频一区二区在线| 国产色无码专区在线观看| 亚洲国产精品毛片在线看| 忘忧草日本在线播放www| 蜜臀av色欲a片无人一区| 亚洲精品网站在线观看不卡无广告| 看全色黄大黄大色免费久久 | 天天影视色香欲综合久久| 少妇激情一区二区三区视频小说 | 韩国美女福利视频在线观看| 亚洲精品麻豆一二三区| 亚洲少妇人妻无码视频| 亚洲精品国男人在线视频| 中国china露脸自拍性hd| 国产精品一品二区三区日韩| 亚洲高潮喷水无码AV电影| 亚洲综合一区二区精品导航| 欧美一区二区| 婷婷五月综合丁香在线| 日韩有码av中文字幕|