主執行緒與守護執行緒
默認情況下,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(鎖)

程序:
-
首先用一個指標試圖將 obj 物件與作業系統中的 Monitor 物件關聯,在正常狀態下,Mark Word 存盤了 hashcode,age等資訊,并且加鎖狀態碼為 “01” 表示并未與任何鎖關聯,但是一旦獲得了鎖,加鎖狀態碼會改為 “10” 并且拋棄掉存盤的 hashcode ,age 等資訊,轉而存盤一個指向 Monitor 物件的指標(ptr_to_heavyweight_monitor),占30位
-
此時執行緒 Thread-2 指向 Monitor 中的 Owner 表示自己是這把鎖現在的主人
-
當一個新的執行緒到來時(Thread-1),會先去檢查此物件有沒有關聯 Monitor 物件,發現已經關聯,繼而檢查 Monitor 物件中的 Owner 已經是 Thread-2 了,此時 Thread-1會跟Monitor 中的 EntryList(阻塞佇列) 關聯,進入 BLOCK 狀態

-
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));
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/488700.html
標籤:其他
