1. 前言
1.1 并發(fā)編程中存在線程安全問(wèn)題
-
存在共享數(shù)據(jù);
-
多線程共同操作共享數(shù)。
關(guān)鍵字 synchronized
可以保證在同一時(shí)刻,只有一個(gè)線程可以執(zhí)行某個(gè)方法或某個(gè)代碼塊,同時(shí) synchronized
可以保證一個(gè)線程的變化可見(jiàn)(可見(jiàn)性),即可以代替 volatile。
1.2 設(shè)計(jì)同步器的意義
多線程編程中,有可能會(huì)出現(xiàn)多個(gè)線程同時(shí)訪問(wèn)同一個(gè)共享、可變資源的情況,這個(gè)資源我們稱(chēng)之為臨界資源
。這種資源可能是:對(duì)象、變量、文件等。
-
共享:資源可以由多個(gè)線程同時(shí)訪問(wèn);
-
可變:資源可以在其生命周期內(nèi)被修改。
引出的問(wèn)題:由于線程執(zhí)行的過(guò)程是不可控的,所以需要采用同步機(jī)制來(lái)協(xié)調(diào)對(duì)對(duì)象可變狀態(tài)的訪問(wèn)。
1.3 如何解決線程并發(fā)安全問(wèn)題?
實(shí)際上,所有的并發(fā)模式在解決線程安全問(wèn)題時(shí),采用的方案都是序列化訪問(wèn)臨界資源
。即在同一時(shí)刻,只能有一個(gè)線程訪問(wèn)臨界資源,也稱(chēng)作同步互斥訪問(wèn)
。
Java 中,提供了兩種方式來(lái)實(shí)現(xiàn)同步互斥訪問(wèn):synchronized
和 Lock
。
同步器的本質(zhì)就是加鎖。加鎖目的:序列化訪問(wèn)臨界資源,即同一時(shí)刻只能有一個(gè)線程訪問(wèn)臨界資源(同步互斥訪問(wèn))。
不過(guò)有一點(diǎn)需要區(qū)別的是:當(dāng)多個(gè)線程執(zhí)行一個(gè)方法時(shí),該方法內(nèi)部的局部變量并不是臨界資源,因?yàn)檫@些局部變量是在每個(gè)線程的私有棧中,并不具有共享性,不會(huì)導(dǎo)致線程安全問(wèn)題。
2. synchronized 的用法
synchronized 內(nèi)置鎖是一種對(duì)象鎖
(鎖的是對(duì)象而非引用),作用粒度是對(duì)象
,可以用來(lái)實(shí)現(xiàn)對(duì)臨界資源的同步互斥
訪問(wèn),是可重入
的。
2.1 修飾實(shí)例方法,鎖是當(dāng)前實(shí)例對(duì)象
public class Juc_LockOnThisObject {
private Integer stock = 10;
public synchronized void decrStock() {
--stock;
System.out.println(ClassLayout.parseInstance(this).toPrintable());
}
}
synchronized 修飾非靜態(tài)方法鎖定的是該類(lèi)的實(shí)例,同一實(shí)例在多線程中調(diào)用才會(huì)觸發(fā)同步鎖定 所以多個(gè)被 synchronized 修飾的非靜態(tài)方法在同一實(shí)例下只能多線程同時(shí)調(diào)用一個(gè)。
2.2 修飾靜態(tài)方法,鎖是當(dāng)前類(lèi)對(duì)象
public class Juc_LockOnClass {
private static int stock;
public static synchronized void decrStock(){
System.out.println(--stock);
}
}
synchronized 修飾靜態(tài)方法鎖定的是類(lèi)本身,而不是實(shí)例,會(huì)作用于類(lèi)的所有對(duì)象實(shí)例 ,進(jìn)入同步代碼前要獲得當(dāng)前 class 的鎖。因?yàn)殪o態(tài)成員不屬于任何一個(gè)實(shí)例對(duì)象,是類(lèi)成員( static 表明這是該類(lèi)的一個(gè)靜態(tài)資源,不管 new 了多少個(gè)對(duì)象,只有一份)。所以,如果一個(gè)線程 A 調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài) synchronized 方法,而線程 B 需要調(diào)用這個(gè)實(shí)例對(duì)象所屬類(lèi)的靜態(tài) synchronized 方法,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問(wèn)靜態(tài) synchronized 方法占用的鎖是當(dāng)前類(lèi)的鎖,而訪問(wèn)非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對(duì)象鎖。
2.3 修飾代碼塊,鎖是括號(hào)里面的對(duì)象
public class Juc_LockOnObject {
public static Object object = new Object();
private Integer stock = 10;
public void decrStock() {
// T1,T2
synchronized (object) {
--stock;
if (stock <= 0) {
System.out.println("庫(kù)存售罄");
return;
}
}
}
}
synchronized 指定加鎖對(duì)象,對(duì)給定對(duì)象/類(lèi)加鎖。synchronized(this|object)
表示進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。synchronized(類(lèi).class)
表示進(jìn)入同步代碼前要獲得當(dāng)前 class 的鎖。
2.4 總結(jié)
-
synchronized
關(guān)鍵字加到 static 靜態(tài)方法和synchronized(class)
代碼塊上都是是給 Class 類(lèi)上鎖。 -
synchronized
關(guān)鍵字加到實(shí)例方法上是給對(duì)象實(shí)例上鎖。
一起看一個(gè) synchronized 使用經(jīng)典實(shí)例—— 線程安全的單例模式:
public class LazySingleton {
/**
* 提供一個(gè)私有的靜態(tài)屬性,對(duì)于 volatile 修飾的字段,可以防止指令重排
*/
private volatile static LazySingleton instance = null;
/**
* 提供一個(gè)私有構(gòu)造函數(shù):避免類(lèi)在外部被實(shí)例化
*/
private LazySingleton() {
}
public static LazySingleton getInstance() {
// 先判斷對(duì)象是否已經(jīng)實(shí)例過(guò),沒(méi)有實(shí)例化過(guò)才進(jìn)入加鎖代碼
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
// 類(lèi)對(duì)象加鎖
instance = new LazySingleton();
}
}
}
return instance;
}
}
3. synchronized 的實(shí)現(xiàn)原理
3.1 synchronized 是怎么加鎖的?
synchronized
是基于 JVM內(nèi)置鎖
實(shí)現(xiàn),通過(guò)內(nèi)部對(duì)象 Monitor(監(jiān)視器鎖)
實(shí)現(xiàn),基于進(jìn)入與退出 Monitor 對(duì)象實(shí)現(xiàn)方法與代碼塊同步,監(jiān)視器鎖的實(shí)現(xiàn)依賴(lài)底層操作系統(tǒng)的 Mutex lock(互斥鎖) 實(shí)現(xiàn),它是一個(gè)重量級(jí)鎖,性能較低。當(dāng)然,JVM 內(nèi)置鎖在1.5之后版本做了大量的優(yōu)化,如鎖粗化(Lock Coarsening)、鎖消除(Lock Eliminaction)、輕量級(jí)鎖(Lightweight Locking)、偏向鎖(Biased Locking)、適應(yīng)性自旋(Adaptive Spinning)等技術(shù)來(lái)減少鎖操作的開(kāi)銷(xiāo),內(nèi)置鎖的并發(fā)性能已經(jīng)基本與 Lock 持平。
(1)synchronized 修飾代碼塊時(shí)
/**
* @author pointer
* @date 2023-04-21 21:16:47
*/
public class SynchronizedDemo {
public void method() {
synchronized (this) {
System.out.println("synchronized 代碼塊");
}
}
}
通過(guò) JDK 自帶的 javap 命令查看 SynchronizedDemo 類(lèi)的相關(guān)字節(jié)碼信息:首先切換到類(lèi)的對(duì)應(yīng)目錄執(zhí)行 javac SynchronizedDemo.java
命令生成編譯后的 .class
文件,然后執(zhí)行 javap -c -s -v -l SynchronizedDemo.class
。
synchronized
修飾代碼塊時(shí),JVM 采用 monitorenter
、monitorexit
兩個(gè)指令來(lái)實(shí)現(xiàn)同步,其中 monitorenter
指令指向同步代碼塊的開(kāi)始位置, monitorexit
指令則指向同步代碼塊的結(jié)束位置。
(2)synchronized 修飾實(shí)例方法
/**
* @author pointer
* @date 2023-04-21 21:16:47
*/
public class SynchronizedDemo {
public synchronized void method() {
System.out.println("synchronized 實(shí)例方法");
}
}
反編譯一下:
synchronized
修飾的方法并沒(méi)有 monitorenter 指令和 monitorexit 指令,取得代之的確實(shí)是 ACC_SYNCHRONIZED
標(biāo)識(shí),該標(biāo)識(shí)指明了該方法是一個(gè)同步方法。JVM 通過(guò)該 ACC_SYNCHRONIZED
訪問(wèn)標(biāo)志來(lái)辨別一個(gè)方法是否聲明為同步方法,從而執(zhí)行相應(yīng)的同步調(diào)用。
3.2 synchronized 同步概念
monitorenter、monitorexit 或者 ACC_SYNCHRONIZED 都是基于 Monitor(監(jiān)視器鎖)
實(shí)現(xiàn)的。
實(shí)例對(duì)象結(jié)構(gòu)里有對(duì)象頭,對(duì)象頭里面有一塊結(jié)構(gòu)叫 Mark Word,Mark Word 指針指向了 Monitor。
(1)Java 對(duì)象頭
在 HotSpot 虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding):
synchronized
是悲觀鎖,在操作同步資源之前需要給同步資源先加鎖,這把鎖就是存在 Java對(duì)象頭
里的,Java對(duì)象頭是什么呢?
我們以 Hotspot 虛擬機(jī)為例,Hotspot 的對(duì)象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類(lèi)型指針):
Mark Word: Mark Word存儲(chǔ)對(duì)象自身的運(yùn)行數(shù)據(jù),如哈希碼、GC分代年齡、鎖狀態(tài)標(biāo)志、偏向時(shí)間戳(Epoch) 等信息。這些信息都是與對(duì)象自身定義無(wú)關(guān)的數(shù)據(jù),所以 Mark Word 被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲(chǔ)盡量多的數(shù)據(jù)。它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間,也就是說(shuō)在運(yùn)行期間 Mark Word 里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。
Klass Point: 對(duì)象指向它的類(lèi)元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例。
(2)監(jiān)視器鎖(Monitor)
監(jiān)視器鎖(Monitor) 本質(zhì)是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來(lái)實(shí)現(xiàn)的。每個(gè)對(duì)象都對(duì)應(yīng)于一個(gè)可稱(chēng)為 “互斥鎖” 的標(biāo)記,這個(gè)標(biāo)記用來(lái)保證在任一時(shí)刻,只能有一個(gè)線程訪問(wèn)該對(duì)象。
互斥鎖:用于保護(hù)臨界區(qū),確保同一時(shí)間只有一個(gè)線程訪問(wèn)數(shù)據(jù)。對(duì)共享資源的訪問(wèn),先對(duì)互斥量進(jìn)行加鎖,如果互斥量已經(jīng)上鎖,調(diào)用線程會(huì)阻塞,直到互斥量被解鎖。在完成了對(duì)共享資源的訪問(wèn)后,要對(duì)互斥量進(jìn)行解鎖。
- mutex的工作方式:
- 申請(qǐng) mutex;
- 如果成功,則持有該mutex;
- 如果失敗,則進(jìn)行spin自旋。spin的過(guò)程就是在線等待mutex,不斷發(fā)起mutex gets, 直到獲得mutex或者達(dá)到spin_count限制為止;
- 依據(jù)工作模式的不同選擇yiled還是sleep;
- 若達(dá)到sleep限制或者被主動(dòng)喚醒或者完成yield,則重復(fù)1-4 步,直到獲得為止。
由于Java的線程是映射到操作系統(tǒng)的原生線程之上的,如果要阻塞或喚醒一條線程,都需要操作系統(tǒng)來(lái)幫忙完成,這就需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài)中,因此狀態(tài)轉(zhuǎn)換需要耗費(fèi)很多的處理器時(shí)間。所以synchronized是Java語(yǔ)言中的一個(gè)重量級(jí)操作。在JDK1.6中,虛擬機(jī)進(jìn)行了一些優(yōu)化,譬如在通知操作系統(tǒng)阻塞線程之前加入一段自旋等待過(guò)程,避免頻繁地切入到核心態(tài)中;
synchronized與java.util.concurrent包中的ReentrantLock相比,由于JDK1.6中加入了針對(duì)鎖的優(yōu)化措施(見(jiàn)后面),使得synchronized與ReentrantLock的性能基本持平。ReentrantLock只是提供了synchronized更豐富的功能,而不一定有更優(yōu)的性能,所以在synchronized能實(shí)現(xiàn)需求的情況下,優(yōu)先考慮使用synchronized來(lái)進(jìn)行同步。
3.3 synchronized 的特性
3.3.1 原子性
原子性就是指一個(gè)操作或者多個(gè)操作,要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷,要么就都不執(zhí)行。被 synchronized
修飾的類(lèi)或?qū)ο蟮乃胁僮鞫际窃拥?,因?yàn)樵趫?zhí)行操作之前必須先獲得類(lèi)或?qū)ο蟮逆i,直到執(zhí)行完才能釋放。
3.3.2 可見(jiàn)性
可見(jiàn)性是指多個(gè)線程訪問(wèn)一個(gè)資源時(shí),該資源的狀態(tài)、值信息等對(duì)于其他線程都是可見(jiàn)的。synchronized
和 volatile
都具有可見(jiàn)性。
synchronized怎么保證可見(jiàn)性?
-
線程加鎖前,將清空工作內(nèi)存中共享變量的值,從而使用共享變量時(shí)需要從主內(nèi)存中重新讀取最新的值;
-
線程加鎖后,其它線程無(wú)法獲取主內(nèi)存中的共享變量;
-
線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存中。
3.3.3 有序性
有序性指的是對(duì)于一個(gè)線程的執(zhí)行代碼,從前往后依次執(zhí)行,單線程下可以認(rèn)為程序是有序的,但是并發(fā)時(shí)有可能會(huì)發(fā)生指令重排。
synchronized怎么保證有序性?
-
synchronized 同步的代碼塊,具有排他性,一次只能被一個(gè)線程擁有,所以 synchronized 保證同一時(shí)刻,代碼是單線程執(zhí)行的;
-
因?yàn)?as-if-serial 語(yǔ)義的存在,單線程的程序能保證最終結(jié)果是有序的,但是不保證不會(huì)指令重排;
-
所以 synchronized 保證的有序是執(zhí)行結(jié)果的有序性,而不是防止指令重排的有序性。
3.3.4 可重入
可重入特性指的是同一個(gè)線程獲得鎖之后,可以再次獲取該鎖(一個(gè)線程可以多次執(zhí)行 synchronized,重復(fù)獲取同一把鎖)。
public class NewThread {
static class MyThread extends Thread {
@Override
public void run() {
synchronized (NewThread.class) {
System.out.println("我是 run");
}
test01();
}
void test01(){
synchronized (NewThread.class) {
System.out.println("我是 test01");
}
}
}
public static void main(String[] args) {
MyThread myThread01 = new MyThread();
MyThread myThread02 = new MyThread();
myThread01.start();
myThread02.start();
}
}
- 原理
synchronized
的鎖對(duì)象中有一個(gè)計(jì)數(shù)器(recursions變量)會(huì)記錄線程獲得幾次鎖,每重入一次,計(jì)數(shù)器就 + 1,在執(zhí)行完一個(gè)同步代碼塊時(shí),計(jì)數(shù)器數(shù)量就會(huì)減 1,直到計(jì)數(shù)器的數(shù)量為 0 才釋放這個(gè)鎖。
- 優(yōu)點(diǎn)
可以避免死鎖(如果不能重入,那就不能再次進(jìn)入這個(gè)同步代碼塊,導(dǎo)致死鎖);更好地封裝代碼(可以把同步代碼塊寫(xiě)入到一個(gè)方法中,然后在另一個(gè)同步代碼塊中直接調(diào)用該方法實(shí)現(xiàn)可重入)。
3.3.5 非公平
非公平是指多個(gè)線程在搶占鎖時(shí)JVM并不會(huì)保證線程先來(lái)后到的順序,非公平性可以提升吞吐量,因?yàn)樯倭司S護(hù)線程順序的開(kāi)銷(xiāo)。
4. 鎖優(yōu)化
synchronized 是通過(guò)對(duì)象內(nèi)部的一個(gè)叫做監(jiān)視器鎖(monitor)來(lái)實(shí)現(xiàn)的,監(jiān)視器鎖本質(zhì)又是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來(lái)實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,這就是為什么 synchronized 效率低的原因。因此,這種依賴(lài)于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱(chēng)之為 “重量級(jí)鎖”。
Java SE 1.6 為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗,引入了 “偏向鎖” 和 “輕量級(jí)鎖”:鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài)
。鎖可以升級(jí)但不能降級(jí)
。
先看一下四種鎖狀態(tài)對(duì)應(yīng)的的Mark Word內(nèi)容,然后再分別講解四種鎖狀態(tài)的思路以及特點(diǎn):
鎖狀態(tài) | 存儲(chǔ)內(nèi)容 | 存儲(chǔ)內(nèi)容 |
---|---|---|
無(wú)鎖 | 對(duì)象的hashCode、對(duì)象分代年齡、是否是偏向鎖(0) | 01 |
偏向鎖 | 偏向線程ID、偏向時(shí)間戳、對(duì)象分代年齡、是否是偏向鎖(1) | 01 |
輕量級(jí)鎖 | 指向棧中鎖記錄的指針 | 00 |
重量級(jí)鎖 | 指向互斥量(重量級(jí)鎖)的指針 | 10 |
4.1 無(wú)鎖
無(wú)鎖沒(méi)有對(duì)資源進(jìn)行鎖定,所有的線程都能訪問(wèn)并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功。
無(wú)鎖的特點(diǎn)就是修改操作在循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源。如果沒(méi)有沖突就修改成功并退出,否則就會(huì)繼續(xù)循環(huán)嘗試。如果有多個(gè)線程修改同一個(gè)值,必定會(huì)有一個(gè)線程能修改成功,而其他修改失敗的線程會(huì)不斷重試直到修改成功。上面我們介紹的CAS原理及應(yīng)用即是無(wú)鎖的實(shí)現(xiàn)。無(wú)鎖無(wú)法全面代替有鎖,但無(wú)鎖在某些場(chǎng)合下的性能是非常高的。
4.2 偏向鎖
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。在大多數(shù)情況下,鎖總是由同一線程多次獲得,不存在多線程競(jìng)爭(zhēng),所以出現(xiàn)了偏向鎖。其目標(biāo)就是在只有一個(gè)線程執(zhí)行同步代碼塊時(shí)能夠提高性能。
當(dāng)一個(gè)線程訪問(wèn)同步代碼塊并獲取鎖時(shí),會(huì)在Mark Word里存儲(chǔ)鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時(shí)不再通過(guò)CAS操作來(lái)加鎖和解鎖,而是檢測(cè)Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。引入偏向鎖是為了在無(wú)多線程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑,因?yàn)檩p量級(jí)鎖的獲取及釋放依賴(lài)多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時(shí)候依賴(lài)一次CAS原子指令即可。
偏向鎖只有遇到其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,線程不會(huì)主動(dòng)釋放偏向鎖。偏向鎖的撤銷(xiāo),需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài)。撤銷(xiāo)偏向鎖后恢復(fù)到無(wú)鎖(標(biāo)志位為“01”)或輕量級(jí)鎖(標(biāo)志位為“00”)的狀態(tài)。
偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的??梢酝ㄟ^(guò)JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。
4.3 輕量級(jí)鎖
是指當(dāng)鎖是偏向鎖的時(shí)候,被另外的線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。
在代碼進(jìn)入同步塊的時(shí)候,如果同步對(duì)象鎖狀態(tài)為無(wú)鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,然后拷貝對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。
拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為指向Lock Record的指針,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word。
如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,表示此對(duì)象處于輕量級(jí)鎖定狀態(tài)。
如果輕量級(jí)鎖的更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說(shuō)明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行,否則說(shuō)明多個(gè)線程競(jìng)爭(zhēng)鎖。
若當(dāng)前只有一個(gè)等待線程,則該線程通過(guò)自旋進(jìn)行等待。但是當(dāng)自旋超過(guò)一定的次數(shù),或者一個(gè)線程在持有鎖,一個(gè)在自旋,又有第三個(gè)來(lái)訪時(shí),輕量級(jí)鎖升級(jí)為重量級(jí)鎖。
4.4 重量級(jí)鎖
升級(jí)為重量級(jí)鎖時(shí),鎖標(biāo)志的狀態(tài)值變?yōu)?10,此時(shí)Mark Word中存儲(chǔ)的是指向重量級(jí)鎖的指針,此時(shí)等待鎖的線程都會(huì)進(jìn)入阻塞狀態(tài)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-434642.html
4.5 鎖升級(jí)全過(guò)程
參考文章:
https://zhuanlan.zhihu.com/p/29866981 - Java synchronized原理總結(jié)
https://www.cnblogs.com/xfeiyun/p/15871647.html#gallery-6 - Java內(nèi)置同步鎖synchronized深入理解
https://www.cnblogs.com/three-fighter/p/14396208.html - synchronized詳解文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-434642.html
到了這里,關(guān)于【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!