java鎖synchronized面試常問(wèn)總結(jié)
synchronized都問(wèn)啥?
如果Java面試有什么是必問(wèn)的,synchronized必定占據(jù)一席之地。初出茅廬時(shí)synchronized的用法,成長(zhǎng)后synchronized的原理,可謂是Java工程師的“一生之?dāng)?rdquo;。
按照慣例,先來(lái)看synchronized的常見(jiàn)問(wèn)題(在線Excel同步更新中):

根據(jù)統(tǒng)計(jì)數(shù)據(jù)可以總結(jié)出synchronized的5大考點(diǎn):
- synchronized的使用方式:
- synchronized是什么?
- synchronized怎么用?
- 不同用法都有什么效果?
- synchronized的實(shí)現(xiàn)原理:
- synchronized的特性是如何實(shí)現(xiàn)的?
- synchronized鎖升級(jí)的原理。
今天我們先來(lái)看synchronized的基礎(chǔ)部分。
synchronized是什么?
synchronized是Java中的關(guān)鍵字,提供了原生同步機(jī)制,實(shí)現(xiàn)互斥語(yǔ)義和可見(jiàn)性保證,通常稱為互斥鎖。
- 互斥指的是,當(dāng)線程獲取到鎖后,其它試圖獲取鎖的線程只能阻塞;
- 可見(jiàn)性指的是,
synchronized修飾的語(yǔ)句內(nèi)修改共享變量可以立即被其它線程獲取。
互斥就意味著,同一時(shí)間只有一個(gè)線程執(zhí)行synchronized修飾的代碼,那么:
- 無(wú)論怎么重排序,都會(huì)遵循as-if-serial語(yǔ)義,因此
synchronized中不存在有序性問(wèn)題; - 不主動(dòng)釋放鎖,其他線程無(wú)法執(zhí)行
synchronized中代碼,無(wú)需考慮原子性問(wèn)題。
因此synchronized中互斥就代表了對(duì)有序性問(wèn)題和原子性問(wèn)題的保證。不過(guò)前提是JSR-133中反復(fù)提到的correctly synchronized(正確的同步),舉個(gè)例子:
public class IncorrectlySynchronized {
private Integer count = 0;
public void add() {
synchronized (count) {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
IncorrectlySynchronized incorrectlySynchronized = new IncorrectlySynchronized();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
incorrectlySynchronized.add();
}
});
Thread t2 = new Thread(()-> {
for (int i = 0; i < 10000; i++) {
incorrectlySynchronized.add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(incorrectlySynchronized.count);
}
}
看似該加synchronized的地方都加了,但是結(jié)果卻會(huì)出乎意料,這就典型的錯(cuò)誤同步的例子。
synchronized鎖什么?
既然是鎖,那么synchronized鎖的是什么呢?
《The Java Language Specification》中描述(節(jié)選)到:
Each object in Java is associated with a monitor, which a thread can lock or unlock. The synchronized statement computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed.
Java中每個(gè)對(duì)象都與一個(gè)監(jiān)視器關(guān)聯(lián),線程可以鎖定或者解鎖該監(jiān)視器。synchronized語(yǔ)句嘗試鎖定與對(duì)象關(guān)聯(lián)的監(jiān)視器,鎖定成功后才可以繼續(xù)執(zhí)行。
通常,我們將synchronized鎖定與對(duì)象關(guān)聯(lián)的監(jiān)視器理解為synchronized鎖定對(duì)象本身。
在我們知道synchronized鎖什么后,再去看用法,很多內(nèi)容就會(huì)一目了然了。
synchronized怎么用?
作為關(guān)鍵字,synchronized有兩種用法:
- 修飾代碼塊
- 修飾方法
- 修飾成員方法
- 修飾靜態(tài)方法
之前有個(gè)同事特別迷信“背技術(shù)”,為了區(qū)分不同用法的效果,背了某機(jī)構(gòu)的“線程八鎖”,但每過(guò)一段時(shí)間就會(huì)忘記。
其實(shí),知道了synchronized鎖什么,不同用法的效果自然就出來(lái)了,看一個(gè)例子:
public class SynchronizedDemo {
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
Thread t1 = new Thread(synchronizedDemo::lockMemberMethod1);
Thread t2 = new Thread(synchronizedDemo::lockMemberMethod2);
t1.start();
// 確保t1先執(zhí)行
TimeUnit.SECONDS.sleep(1);
t2.start();
}
private synchronized void lockMemberMethod1() {
System.out.println("方法1");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void lockMemberMethod2() {
System.out.println("方法2");
}
}
通過(guò)實(shí)例變量調(diào)用成員方法時(shí),會(huì)隱式的傳遞this。這個(gè)例子中,t1和t2想鎖定的監(jiān)視器是誰(shuí)的?synchronizedDemo對(duì)象的。t1先獲取到,那么t2只能等待t1釋放后再獲取了。
那此時(shí)的鎖定范圍是什么?synchronizedDemo對(duì)象。
修改下代碼:
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
SynchronizedDemo synchronizedDemo2 = new SynchronizedDemo();
Thread t1 = new Thread(synchronizedDemo::lockMemberMethod1);
Thread t2 = new Thread(synchronizedDemo2::lockMemberMethod2);
t1.start();
t2.start();
}
t2不再爭(zhēng)奪synchronizedDemo而是爭(zhēng)奪synchronizedDemo2,結(jié)果上也能看出t1和t2之間不存在競(jìng)爭(zhēng)關(guān)系。
那么使用synchronized修飾靜態(tài)方法和代碼塊是什么效果呢?
private static synchronized void lockStaticMethod() {
System.out.println("靜態(tài)方法!");
}
private void lockCodeBlock(int count) {
synchronized (this) {
System.out.println("成員方法的代碼塊!");
}
}
使用synchronized修飾靜態(tài)方法,鎖定的對(duì)象是SynchronizedDemo.class。所有SynchronizedDemo的實(shí)例對(duì)象共用同一個(gè)SynchronizedDemo.class,同一時(shí)間不同變量,只有一個(gè)線程可以執(zhí)行lockStaticMethod方法。
至于synchronized修飾代碼塊,就比較靈活了,括號(hào)中是誰(shuí)就鎖定誰(shuí)。如果是this就鎖定實(shí)例變量,如果是SynchronizedDemo.class效果就和修飾靜態(tài)方法一樣。
至于前面錯(cuò)誤的同步的例子,它的問(wèn)題是count對(duì)象在不斷變化(Integer實(shí)現(xiàn)相關(guān))的,因此synchronized鎖定的并不是同一個(gè)對(duì)象。
結(jié)語(yǔ)
今天的內(nèi)容非?;A(chǔ),難度也不大。
重點(diǎn)可以放在synchronized鎖什么的部分,以及是如何推導(dǎo)出synchronized不同用法產(chǎn)生的不同效果的。這樣的方式更接近于問(wèn)題的本質(zhì),也能更好的舉一反三,而不是死記硬背“線程八鎖”這種東西。
以上就是java鎖synchronized面試常問(wèn)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于java synchronized面試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
Java實(shí)現(xiàn)線程按序交替執(zhí)行的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)線程按序交替執(zhí)行,文中的示例代碼講解詳細(xì),對(duì)我們了解線程有一定幫助,需要的可以參考一下2022-10-10
springboot+vue前后端分離項(xiàng)目中使用jwt實(shí)現(xiàn)登錄認(rèn)證
本文介紹了如何在SpringBoot+Vue前后端分離的項(xiàng)目中使用JWT實(shí)現(xiàn)登錄認(rèn)證,內(nèi)容包括后端的響應(yīng)工具類、JWT工具類、登錄用戶實(shí)體類、登錄接口、測(cè)試接口、過(guò)濾器、啟動(dòng)類以及前端的登錄頁(yè)面實(shí)現(xiàn),感興趣的可以了解一下2024-10-10
Spring之ShutDown?Hook死鎖現(xiàn)象解讀
這篇文章主要介紹了Spring之ShutDown?Hook死鎖現(xiàn)象解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
spring cloud gateway請(qǐng)求跨域問(wèn)題解決方案
這篇文章主要介紹了spring cloud gateway請(qǐng)求跨域問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01

