淺析synchronized和Lock
1.寫在前面
在最近的一次面試中,被面試官問到了synchronized已經能夠保證一個線程可以同步訪問代碼塊,爲何還要單獨的提供Lock接口呢?今天,咱們就來一塊兒探討下這個問題。
2.死鎖問題
在談論synchronized和Lock以前,咱們先來看一下死鎖的問題。若是要發生死鎖,則必須存在如下四個必要條件,缺一不可。
- 互斥條件:
在一段時間內某資源僅爲一個線程所佔有。此時如有其餘線程請求該資源,則請求線程只能等待。
- 不可剝奪條件:
線程所得到的資源在未使用完畢以前,不能被其餘線程強行奪走,即只能由得到該資源的線程本身來釋放(只能是主動釋放)。
- 請求與保持條件:
線程已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其餘線程佔有,此時請求線程被阻塞,但對本身已得到的資源保持不放。
- 循環等待條件:
在發生死鎖時必然存在一個進程等待隊列{P1,P2,…,Pn},其中P1等待P2佔有的資源,P2等待P3佔有的資源,…,Pn等待P1佔有的資源,造成一個進程等待環路,環路中每個進程所佔有的資源同時被另外一個申請,也就是前一個進程佔有後一個進程所深情地資源。
3.synchronize的侷限性
當咱們使用synchronize關鍵字發生了死鎖的時候,synchronize關鍵是沒有辦法破壞「不可剝奪」條件的。這是由於若是synchronize申請資源時候,沒有申請到,線程直接進入阻塞狀態,從而什麼都作不了,也相應的沒法釋放資源。面試
4.鎖問題的解決
瞭解到synchronize的侷限性,咱們在設計一把鎖的時候就應該考慮到下面這些特性。
特性 |
描述 |
嘗試性的非阻塞的獲取鎖 |
當前線程嘗試的獲取鎖,若是這一時段沒有被q其餘線程獲取,則成功的獲取鎖,不然直接返回false |
能被中斷的獲取鎖 |
若是阻塞狀態的線程可以響應中斷信號, 也就是說當咱們給阻塞的線程發送中斷信號的時候, 可以喚醒它, 那它就有機會釋放曾經持有的鎖A。這樣就破壞了不可剝奪條件了。 |
超時獲取鎖 |
若是線程在一段時間以內沒有獲取到鎖, 不是進入阻塞狀態, 而是返回一個錯誤, 那這個線程也有機會釋放曾經持有的鎖。這樣也能破壞不可剝奪條件。 |
5.Lock鎖的特性
5.1嘗試性非阻塞地獲取鎖(tryLock)
- tryLock()方法
tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,若是獲取成功,則返回true,若是獲取失敗(即鎖已被其餘線程獲取),則返回false,也就說這個方法不管如何都會當即返回。在拿不到鎖時不會一直在那等待
- tryLock(long time, TimeUnit unit)方法
tryLock(long time, TimeUnit unit)方法和tryLock()方法是相似的,只不過區別在於這個方法在拿不到鎖時會等待必定的時間,在時間期限以內若是還拿不到鎖,就返回false。若是一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。
這樣看來,Lock就已經解決了對於不可剝奪條件的破壞併發
5.2能被中斷的獲取鎖(lockInterruptibly()throws InterruptedException)
與synchronized不一樣,獲取到鎖的線程可以響應中斷,當獲取到鎖的線程被中斷時,中斷異常將會被拋出,同時鎖會被釋放。
- 當前線程獲取鎖以前(並未參與獲取鎖)被其餘線程標記interrupt中斷,當調用此方法時直接拋出中斷異常。
- 當前線程獲取鎖,而且鎖被其餘線程持有,則一直阻塞,此時其餘線程來中斷此線程,則會拋出中斷異常。
5.3超時獲取鎖(tryLock(long time,TimeUtil unit)throws InterruptedException)
在指定的時間內可以獲取鎖,超出時間仍熱沒法獲取,則返回
- 當前線程在指定時間內獲取了鎖。
- 當前線程在指定時間內被中斷,鎖被釋放。
- 當前線程在超出指定的時間,則直接返回false。
6.總結
- Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現,synchronized是在JVM層面上實現的,不但能夠經過一些監控工具監控synchronized的鎖定,並且在代碼執行時出現異常,JVM會自動釋放鎖定,可是使用Lock則不行,lock是經過代碼實現的,要保證鎖定必定會被釋放,就必須將 unLock()放到finally{} 中;
- synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;
- Lock可讓等待鎖的線程響應中斷,線程能夠中斷去幹別的事務,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;
- synchronize是一把悲觀鎖,Lock是一把樂觀鎖,悲觀鎖和樂觀鎖在併發量低的時候,性能差很少,可是在併發量高的時候, 樂觀鎖的性能遠遠優於悲觀鎖。也就是說併發量大的時候Lock的性能要遠遠好於synchronize的。
- Lock鎖能夠知道是否獲取鎖成功,而對於synchronize來講 這是不可能的