【JAVA併發編程-黑馬】第三章

一、synchronized 原理(字節碼角度)

	static final Object lock = new Object();
	static int counter = 0;
	public static void main(String[] args) {
		synchronized (lock) {
			counter++;
		}
	}

字節碼文件:

public static void main(java.lang.String[]);
	descriptor: ([Ljava/lang/String;)V
	flags: ACC_PUBLIC, ACC_STATIC
	Code:
	stack=2, locals=3, args_size=1
		0: getstatic #2 // <- lock引用 (synchronized開始),拿到lock鎖對象
		3: dup //複製了一份對象
		4: astore_1 // lock引用 -> slot 1   存儲到一個臨時變量,爲了解鎖用
		5: monitorenter // 將 lock對象 MarkWord 置爲 Monitor 指針
		6: getstatic #3 // <- i
		9: iconst_1 // 準備常數 1
		10: iadd // +1
		11: putstatic #3 // -> i
		14: aload_1 // <- lock引用 獲取之前存儲的臨時變量
		15: monitorexit // 將 lock對象 MarkWord 重置, 喚醒 EntryList
		16: goto 24
		19: astore_2 // e -> slot 2
		20: aload_1 // <- lock引用
		21: monitorexit // 將 lock對象 MarkWord 重置, 喚醒 EntryList
		22: aload_2 // <- slot 2 (e)
		23: athrow // throw e
		24: return
	Exception table: //如果發生異常
		from to target type
		6 16 19 any   //這裏是監控6-19,也就是同步代碼塊
		19 22 19 any //執行19-22行,如果發生異常,也會解鎖,因爲在21行將MarkWord重置了。
	LineNumberTable:
		line 8: 0
		line 9: 6
		line 10: 14
		line 11: 24
	LocalVariableTable:
		Start Length Slot Name Signature
		0 25 0 args [Ljava/lang/String;
	StackMapTable: number_of_entries = 2
		frame_type = 255 /* full_frame */
			offset_delta = 19
			locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
			stack = [ class java/lang/Throwable ]
		frame_type = 250 /* chop */
			offset_delta = 4

所以,使用synchronized 如果發生異常了,也不會導致死鎖

二、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
    在這裏插入圖片描述
  • 讓鎖記錄中 Object reference 指向鎖對象,並嘗試用 cas 替換 Object 的 Mark Word,將 Mark Word 的值存
    入鎖記錄
    在這裏插入圖片描述
  • 如果 cas 替換成功,對象頭中存儲了 鎖記錄地址和狀態 00 ,表示由該線程給對象加鎖,這時圖示如下
    在這裏插入圖片描述
  • 如果 cas 失敗,有兩種情況
    1、如果是其它線程已經持有了該 Object 的輕量級鎖,這時表明有競爭,進入鎖膨脹過程
    2、如果是自己執行了 synchronized 鎖重入,那麼再添加一條 Lock Record 作爲重入的計數
    在這裏插入圖片描述
  • 當退出 synchronized 代碼塊(解鎖時)如果有取值爲 null 的鎖記錄,表示有重入,這時重置鎖記錄,表示重入計數減一
    在這裏插入圖片描述
  • 當退出 synchronized 代碼塊(解鎖時)鎖記錄的值不爲 null,這時使用 cas 將 Mark Word 的值恢復給對象頭
    1、成功,則解鎖成功
    2、失敗,說明輕量級鎖進行了鎖膨脹或已經升級爲重量級鎖,進入重量級鎖解鎖流程

2.2 鎖膨脹

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

  • 當 Thread-1 進行輕量級加鎖時,Thread-0 已經對該對象加了輕量級鎖
    在這裏插入圖片描述
  • 這時 Thread-1 加輕量級鎖失敗,進入鎖膨脹流程
    即爲 Object 對象申請 Monitor 鎖,讓 Object 指向重量級鎖地址
    然後自己進入 Monitor 的 EntryList BLOCKED
    在這裏插入圖片描述
    當 Thread-0 退出同步塊解鎖時,使用 cas 將 Mark Word 的值恢復給對象頭,失敗。這時會進入重量級解鎖流程,即按照 Monitor 地址找到 Monitor 對象,設置 Owner 爲 null,喚醒 EntryList 中 BLOCKED 線程

3.3自旋優化

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

自旋重試成功的情況
在這裏插入圖片描述
自旋失敗的情況
在這裏插入圖片描述

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

3.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
	}
}

在這裏插入圖片描述
在這裏插入圖片描述

3.5偏向鎖狀態

先來回憶一下對象頭
在這裏插入圖片描述
一個對象創建時:

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

測試偏向鎖

利用 jol 第三方工具來查看對象頭信息(注意這裏我擴展了 jol 讓它輸出更爲簡潔)

       <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] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101
11:08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101
11:08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101

注意
處於偏向鎖的對象解鎖後,線程 id 仍存儲於對象頭中。

測試禁用

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

輸出

11:13:10.018 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
11:13:10.021 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000
11:13:10.021 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

測試 hashCode

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

3.6 偏向鎖撤銷

1、當一個對象處於偏向狀態的時候,調用一下hashcode方法,就會撤銷偏向狀態

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

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();
}

因爲wait/notify只有重量級鎖有,所以撤銷偏向鎖其實不難理解
輸出

[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

3.7批量重偏向

  • 如果對象雖然被多個線程訪問,但沒有競爭,這時偏向了線程 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

當執行20次時,偏向的線程變了。偏向了t2線程。

2.8批量撤銷

  • 當撤銷偏向鎖閾值超過 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));
}

2.9鎖消除

@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
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

JIT發現這個鎖對象是局部變量,不會存在共享,乾脆就就將鎖消除了。

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