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

根據(jù)統(tǒng)計數(shù)據(jù)可以總結(jié)出synchronized的5大考點:
- synchronized的使用方式:
- synchronized是什么?
- synchronized怎么用?
- 不同用法都有什么效果?
- synchronized的實現(xiàn)原理:
- synchronized的特性是如何實現(xiàn)的?
- synchronized鎖升級的原理。
今天我們先來看synchronized的基礎部分。
synchronized是什么?
synchronized是Java中的關(guān)鍵字,提供了原生同步機制,實現(xiàn)互斥語義和可見性保證,通常稱為互斥鎖。
- 互斥指的是,當線程獲取到鎖后,其它試圖獲取鎖的線程只能阻塞;
- 可見性指的是,
synchronized修飾的語句內(nèi)修改共享變量可以立即被其它線程獲取。
互斥就意味著,同一時間只有一個線程執(zhí)行synchronized修飾的代碼,那么:
- 無論怎么重排序,都會遵循as-if-serial語義,因此
synchronized中不存在有序性問題; - 不主動釋放鎖,其他線程無法執(zhí)行
synchronized中代碼,無需考慮原子性問題。
因此synchronized中互斥就代表了對有序性問題和原子性問題的保證。不過前提是JSR-133中反復提到的correctly synchronized(正確的同步),舉個例子:
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é)果卻會出乎意料,這就典型的錯誤同步的例子。
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中每個對象都與一個監(jiān)視器關(guān)聯(lián),線程可以鎖定或者解鎖該監(jiān)視器。synchronized語句嘗試鎖定與對象關(guān)聯(lián)的監(jiān)視器,鎖定成功后才可以繼續(xù)執(zhí)行。
通常,我們將synchronized鎖定與對象關(guān)聯(lián)的監(jiān)視器理解為synchronized鎖定對象本身。
在我們知道synchronized鎖什么后,再去看用法,很多內(nèi)容就會一目了然了。
synchronized怎么用?
作為關(guān)鍵字,synchronized有兩種用法:
- 修飾代碼塊
- 修飾方法
- 修飾成員方法
- 修飾靜態(tài)方法
之前有個同事特別迷信“背技術(shù)”,為了區(qū)分不同用法的效果,背了某機構(gòu)的“線程八鎖”,但每過一段時間就會忘記。
其實,知道了synchronized鎖什么,不同用法的效果自然就出來了,看一個例子:
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");
}
}
通過實例變量調(diào)用成員方法時,會隱式的傳遞this。這個例子中,t1和t2想鎖定的監(jiān)視器是誰的?synchronizedDemo對象的。t1先獲取到,那么t2只能等待t1釋放后再獲取了。
那此時的鎖定范圍是什么?synchronizedDemo對象。
修改下代碼:
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不再爭奪synchronizedDemo而是爭奪synchronizedDemo2,結(jié)果上也能看出t1和t2之間不存在競爭關(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)方法,鎖定的對象是SynchronizedDemo.class。所有SynchronizedDemo的實例對象共用同一個SynchronizedDemo.class,同一時間不同變量,只有一個線程可以執(zhí)行lockStaticMethod方法。
至于synchronized修飾代碼塊,就比較靈活了,括號中是誰就鎖定誰。如果是this就鎖定實例變量,如果是SynchronizedDemo.class效果就和修飾靜態(tài)方法一樣。
至于前面錯誤的同步的例子,它的問題是count對象在不斷變化(Integer實現(xiàn)相關(guān))的,因此synchronized鎖定的并不是同一個對象。
結(jié)語
今天的內(nèi)容非常基礎,難度也不大。
重點可以放在synchronized鎖什么的部分,以及是如何推導出synchronized不同用法產(chǎn)生的不同效果的。這樣的方式更接近于問題的本質(zhì),也能更好的舉一反三,而不是死記硬背“線程八鎖”這種東西。
以上就是java鎖synchronized面試常問總結(jié)的詳細內(nèi)容,更多關(guān)于java synchronized面試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Java自定義注解實現(xiàn)一個簡單的令牌桶限流器
限流是在分布式系統(tǒng)中常用的一種策略,它可以有效地控制系統(tǒng)的訪問流量,保證系統(tǒng)的穩(wěn)定性和可靠性,在本文中,我將介紹如何使用Java自定義注解來實現(xiàn)一個簡單的令牌桶限流器,需要的朋友可以參考下2023-10-10
SpringBoot整合EasyExcel進行大數(shù)據(jù)處理的方法詳解
EasyExcel是一個基于Java的簡單、省內(nèi)存的讀寫Excel的開源項目。在盡可能節(jié)約內(nèi)存的情況下支持讀寫百M的Excel。本文將在SpringBoot中整合EasyExcel進行大數(shù)據(jù)處理,感興趣的可以了解一下2022-05-05
java核心編程之文件過濾類FileFilter和FilenameFilter
這篇文章主要為大家詳細介紹了java文件過濾類FileFilter和FilenameFilter,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
總結(jié)Java中線程的狀態(tài)及多線程的實現(xiàn)方式
Java中可以通過Thread類和Runnable接口來創(chuàng)建多個線程,線程擁有五種狀態(tài),下面我們就來簡單總結(jié)Java中線程的狀態(tài)及多線程的實現(xiàn)方式:2016-07-07
SpringCloud Webflux過濾器增加header傳遞方式
這篇文章主要介紹了SpringCloud Webflux過濾器增加header傳遞方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02

