synchronized?和?Lock?的異同點(如何讓選擇)
前言:
我們主要學(xué)習(xí) synchronized 和 Lock 的異同點,以及該如何選擇。
相同點
synchronized 和 Lock 的相同點非常多,我們這里重點講解 3 個比較大的相同點。
- synchronized 和 Lock 都是用來保護(hù)資源線程安全的。
這一點毋庸置疑,這是它們的基本作用。
- 都可以保證可見性。
對于 synchronized 而言,線程 A 在進(jìn)入 synchronized 塊之前或在 synchronized 塊內(nèi)進(jìn)行操作,對于后續(xù)的獲得同一個 monitor 鎖的線程 B 是可見的,也就是線程 B 是可以看到線程 A 之前的操作的,這也體現(xiàn)了 happens-before 針對 synchronized 的一個原則。
而對于 Lock 而言,它和 synchronized 是一樣,都可以保證可見性,如圖所示,在解鎖之前的所有操作對加鎖之后的所有操作都是可見的。
如果你之前不了解什么是可見性,此時理解可能會有一定的困難,可以在學(xué)習(xí)本專欄的 Java 內(nèi)存模型相關(guān)內(nèi)容后,再復(fù)習(xí)本課時,就會豁然開朗。
- synchronized 和 ReentrantLock 都擁有可重入的特點。
這里的 ReentrantLock 是 Lock 接口的一個最主要的實現(xiàn)類,在對比 synchronized 和 Lock 的時候,也會選擇 Lock 的主要實現(xiàn)類來進(jìn)行對比??芍厝胫傅氖悄硞€線程如果已經(jīng)獲得了一個鎖,現(xiàn)在試圖再次請求這個它已經(jīng)獲得的鎖,如果它無需提前釋放這個鎖,而是直接可以繼續(xù)使用持有的這個鎖,那么就是可重入的。如果必須釋放鎖后才能再次申請這個鎖,就是不可重入的。而 synchronized 和 ReentrantLock 都具有可重入的特性。
不同點
下面我們來看下 synchronized 和 Lock 的區(qū)別,和相同點一樣,它們之間也有非常多的區(qū)別,這里講解其中比較大的 7 點不同。
用法區(qū)別:
synchronized 關(guān)鍵字可以加在方法上,不需要指定鎖對象(此時的鎖對象為 this),也可以新建一個同步代碼塊并且自定義 monitor 鎖對象;而 Lock 接口必須顯示用 Lock 鎖對象開始加鎖 lock() 和解鎖 unlock(),并且一般會在 finally 塊中確保用 unlock() 來解鎖,以防發(fā)生死鎖。
與 Lock 顯式的加鎖和解鎖不同的是 synchronized 的加解鎖是隱式的,尤其是拋異常的時候也能保證釋放鎖,但是 Java 代碼中并沒有相關(guān)的體現(xiàn)。
加解鎖順序不同:
對于 Lock 而言如果有多把 Lock 鎖,Lock 可以不完全按照加鎖的反序解鎖,比如我們可以先獲取 Lock1 鎖,再獲取 Lock2 鎖,解鎖時則先解鎖 Lock1,再解鎖 Lock2,加解鎖有一定的靈活度,如代碼所示。
lock1.lock(); lock2.lock(); ... lock1.unlock(); lock2.unlock();
但是 synchronized 無法做到,synchronized 解鎖的順序和加鎖的順序必須完全相反,例如:
synchronized(obj1){ synchronized(obj2){ ... } }
那么在這里,順序就是先對 obj1 加鎖,然后對 obj2 加鎖,然后對 obj2 解鎖,最后解鎖 obj1。這是因為 synchronized 加解鎖是由 JVM 實現(xiàn)的,在執(zhí)行完 synchronized 塊后會自動解鎖,所以會按照 synchronized 的嵌套順序加解鎖,不能自行控制。
synchronized 鎖不夠靈活
一旦 synchronized 鎖已經(jīng)被某個線程獲得了,此時其他線程如果還想獲得,那它只能被阻塞,直到持有鎖的線程運行完畢或者發(fā)生異常從而釋放這個鎖。如果持有鎖的線程持有很長時間才釋放,那么整個程序的運行效率就會降低,而且如果持有鎖的線程永遠(yuǎn)不釋放鎖,那么嘗試獲取鎖的線程只能永遠(yuǎn)等下去。
相比之下,Lock 類在等鎖的過程中,如果使用的是 lockInterruptibly 方法,那么如果覺得等待的時間太長了不想再繼續(xù)等待,可以中斷退出,也可以用 tryLock() 等方法嘗試獲取鎖,如果獲取不到鎖也可以做別的事,更加靈活。
- synchronized 鎖只能同時被一個線程擁有,但是 Lock 鎖沒有這個限制
例如在讀寫鎖中的讀鎖,是可以同時被多個線程持有的,可是 synchronized 做不到。
- 原理區(qū)別 synchronized 是內(nèi)置鎖,由 JVM 實現(xiàn)獲取鎖和釋放鎖的原理,還分為偏向鎖、輕量級鎖、重量級鎖。
Lock 根據(jù)實現(xiàn)不同,有不同的原理,例如 ReentrantLock 內(nèi)部是通過 AQS 來獲取和釋放鎖的。
是否可以設(shè)置公平/非公平:
公平鎖是指多個線程在等待同一個鎖時,根據(jù)先來后到的原則依次獲得鎖。ReentrantLock 等 Lock 實現(xiàn)類可以根據(jù)自己的需要來設(shè)置公平或非公平,synchronized 則不能設(shè)置。
性能區(qū)別:
在 Java 5 以及之前,synchronized 的性能比較低,但是到了 Java 6 以后,發(fā)生了變化,因為 JDK 對 synchronized 進(jìn)行了很多優(yōu)化,比如自適應(yīng)自旋、鎖消除、鎖粗化、輕量級鎖、偏向鎖等,所以后期的 Java 版本里的 synchronized 的性能并不比 Lock 差。
如何選擇
講完了 synchronized 和 Lock 的相同點和區(qū)別,最后我們再來看下如何選擇它們,
在 Java 并發(fā)編程實戰(zhàn)和 Java 核心技術(shù)里都認(rèn)為:
- 如果能不用最好既不使用 Lock 也不使用 synchronized。因為在許多情況下你可以使用 java.util.concurrent 包中的機(jī)制,它會為你處理所有的加鎖和解鎖操作,也就是推薦優(yōu)先使用工具類來加解鎖。
- 如果 synchronized 關(guān)鍵字適合你的程序, 那么請盡量使用它,這樣可以減少編寫代碼的數(shù)量,減少出錯的概率。因為一旦忘記在 finally 里 unlock,代碼可能會出很大的問題,而使用 synchronized 更安全。
- 如果特別需要 Lock 的特殊功能,比如嘗試獲取鎖、可中斷、超時功能等,才使用 Lock。
到此這篇關(guān)于 synchronized 和 Lock 的異同點(如何讓選擇)的文章就介紹到這了,更多相關(guān) synchronized 和 Lock 區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入Synchronized和java.util.concurrent.locks.Lock的區(qū)別詳解
- 詳談Lock與synchronized 的區(qū)別
- Java編程synchronized與lock的區(qū)別【推薦】
- 簡單了解synchronized和lock的區(qū)別
- 淺談Synchronized和Lock的區(qū)別
- 通過實例解析synchronized和lock區(qū)別
- Java 多線程Synchronized和Lock的區(qū)別
- 淺談Java中Lock和Synchronized的區(qū)別
- Java常用鎖synchronized和ReentrantLock的區(qū)別
相關(guān)文章
Java中this,static,final,const用法詳解
這篇文章主要介紹了Java中this,static,final,const用法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-07-07Java如何使用while循環(huán)計算一個整數(shù)的位數(shù)
這篇文章主要介紹了Java使用while循環(huán)計算一個整數(shù)的位數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01OpenFeign實現(xiàn)遠(yuǎn)程調(diào)用
這篇文章主要為大家詳細(xì)介紹了OpenFeign實現(xiàn)遠(yuǎn)程調(diào)用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08Java并發(fā)之條件阻塞Condition的應(yīng)用代碼示例
這篇文章主要介紹了Java并發(fā)之條件阻塞Condition的應(yīng)用代碼示例,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02JavaWeb開發(fā)入門第二篇Tomcat服務(wù)器配置講解
JavaWeb開發(fā)入門第二篇主要介紹了Tomcat服務(wù)器配置的方法教大家如何使用Tomcat服務(wù)器,感興趣的小伙伴們可以參考一下2016-04-04logback-spring.xml的內(nèi)容格式詳解
這篇文章主要介紹了logback-spring.xml的內(nèi)容格式詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的的朋友參考下吧2023-11-11