java鎖synchronized面試常問總結(jié)
synchronized都問啥?
如果Java面試有什么是必問的,synchronized
必定占據(jù)一席之地。初出茅廬時(shí)synchronized
的用法,成長后synchronized
的原理,可謂是Java工程師的“一生之?dāng)?rdquo;。
按照慣例,先來看synchronized
的常見問題(在線Excel同步更新中):
根據(jù)統(tǒng)計(jì)數(shù)據(jù)可以總結(jié)出synchronized
的5大考點(diǎn):
- synchronized的使用方式:
- synchronized是什么?
- synchronized怎么用?
- 不同用法都有什么效果?
- synchronized的實(shí)現(xiàn)原理:
- synchronized的特性是如何實(shí)現(xiàn)的?
- synchronized鎖升級的原理。
今天我們先來看synchronized
的基礎(chǔ)部分。
synchronized是什么?
synchronized
是Java中的關(guān)鍵字,提供了原生同步機(jī)制,實(shí)現(xiàn)互斥語義和可見性保證,通常稱為互斥鎖。
- 互斥指的是,當(dāng)線程獲取到鎖后,其它試圖獲取鎖的線程只能阻塞;
- 可見性指的是,
synchronized
修飾的語句內(nèi)修改共享變量可以立即被其它線程獲取。
互斥就意味著,同一時(shí)間只有一個(gè)線程執(zhí)行synchronized
修飾的代碼,那么:
- 無論怎么重排序,都會(huì)遵循as-if-serial語義,因此
synchronized
中不存在有序性問題; - 不主動(dòng)釋放鎖,其他線程無法執(zhí)行
synchronized
中代碼,無需考慮原子性問題。
因此synchronized
中互斥就代表了對有序性問題和原子性問題的保證。不過前提是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è)對象都與一個(gè)監(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)容就會(huì)一目了然了。
synchronized怎么用?
作為關(guān)鍵字,synchronized
有兩種用法:
- 修飾代碼塊
- 修飾方法
- 修飾成員方法
- 修飾靜態(tài)方法
之前有個(gè)同事特別迷信“背技術(shù)”,為了區(qū)分不同用法的效果,背了某機(jī)構(gòu)的“線程八鎖”,但每過一段時(shí)間就會(huì)忘記。
其實(shí),知道了synchronized
鎖什么,不同用法的效果自然就出來了,看一個(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"); } }
通過實(shí)例變量調(diào)用成員方法時(shí),會(huì)隱式的傳遞this
。這個(gè)例子中,t1和t2想鎖定的監(jiān)視器是誰的?synchronizedDemo
對象的。t1先獲取到,那么t2只能等待t1釋放后再獲取了。
那此時(shí)的鎖定范圍是什么?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
的實(shí)例對象共用同一個(gè)SynchronizedDemo.class
,同一時(shí)間不同變量,只有一個(gè)線程可以執(zhí)行lockStaticMethod
方法。
至于synchronized
修飾代碼塊,就比較靈活了,括號(hào)中是誰就鎖定誰。如果是this
就鎖定實(shí)例變量,如果是SynchronizedDemo.class
效果就和修飾靜態(tài)方法一樣。
至于前面錯(cuò)誤的同步的例子,它的問題是count
對象在不斷變化(Integer
實(shí)現(xiàn)相關(guān))的,因此synchronized
鎖定的并不是同一個(gè)對象。
結(jié)語
今天的內(nèi)容非?;A(chǔ),難度也不大。
重點(diǎn)可以放在synchronized
鎖什么的部分,以及是如何推導(dǎo)出synchronized
不同用法產(chǎn)生的不同效果的。這樣的方式更接近于問題的本質(zhì),也能更好的舉一反三,而不是死記硬背“線程八鎖”這種東西。
以上就是java鎖synchronized面試常問總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于java synchronized面試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)
下面小編就為大家?guī)硪黄狫ava 普通代碼塊靜態(tài)代碼塊執(zhí)行順序(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08Java實(shí)現(xiàn)線程按序交替執(zhí)行的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)線程按序交替執(zhí)行,文中的示例代碼講解詳細(xì),對我們了解線程有一定幫助,需要的可以參考一下2022-10-10springboot+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í)體類、登錄接口、測試接口、過濾器、啟動(dòng)類以及前端的登錄頁面實(shí)現(xiàn),感興趣的可以了解一下2024-10-10Spring之ShutDown?Hook死鎖現(xiàn)象解讀
這篇文章主要介紹了Spring之ShutDown?Hook死鎖現(xiàn)象解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04spring cloud gateway請求跨域問題解決方案
這篇文章主要介紹了spring cloud gateway請求跨域問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01