Java并发编程实战~Lock
再造管程的理由
synchronized導(dǎo)致死鎖問題,提出了一個破壞不可搶占條件方案,但是這個方案 synchronized 沒有辦法解決。原因是 synchronized 申請資源的時候,如果申請不到,線程直接進(jìn)入阻塞狀態(tài)了,而線程進(jìn)入阻塞狀態(tài),啥都干不了,也釋放不了線程已經(jīng)占有的資源。但我們希望的是:
對于“不可搶占”這個條件,占用部分資源的線程進(jìn)一步申請其他資源時,如果申請不到,可以主動釋放它占有的資源,這樣不可搶占這個條件就破壞掉了。如果我們重新設(shè)計一把互斥鎖去解決這個問題,那該怎么設(shè)計呢?我覺得有三種方案。
1、能夠響應(yīng)中斷。synchronized 的問題是,持有鎖 A 后,如果嘗試獲取鎖 B 失敗,那么線程就進(jìn)入阻塞狀態(tài),一旦發(fā)生死鎖,就沒有任何機(jī)會來喚醒阻塞的線程。但如果阻塞狀態(tài)的線程能夠響應(yīng)中斷信號,也就是說當(dāng)我們給阻塞的線程發(fā)送中斷信號的時候,能夠喚醒它,那它就有機(jī)會釋放曾經(jīng)持有的鎖 A。這樣就破壞了不可搶占條件了。
2、支持超時。如果線程在一段時間之內(nèi)沒有獲取到鎖,不是進(jìn)入阻塞狀態(tài),而是返回一個錯誤,那這個線程也有機(jī)會釋放曾經(jīng)持有的鎖。這樣也能破壞不可搶占條件。
3、非阻塞地獲取鎖。如果嘗試獲取鎖失敗,并不進(jìn)入阻塞狀態(tài),而是直接返回,那這個線程也有機(jī)會釋放曾經(jīng)持有的鎖。這樣也能破壞不可搶占條件。
這三種方案可以全面彌補(bǔ) synchronized 的問題。到這里相信你應(yīng)該也能理解了,這三個方案就是“重復(fù)造輪子”的主要原因,體現(xiàn)在 API 上,就是 Lock 接口的三個方法。詳情如下:
如何保證可見性
class X {private final Lock rtl = new ReentrantLock();int value;public void addOne() {// 獲取鎖rtl.lock(); try {value+=1;} finally {// 保證鎖能釋放rtl.unlock();}} }ReentrantLock,內(nèi)部持有一個 volatile 的成員變量 state,獲取鎖的時候,會讀寫 state 的值;解鎖的時候,也會讀寫 state 的值(簡化后的代碼如下面所示)。也就是說,在執(zhí)行 value+=1 之前,程序先讀寫了一次 volatile 變量 state,在執(zhí)行 value+=1 之后,又讀寫了一次 volatile 變量 state。根據(jù)相關(guān)的 Happens-Before 規(guī)則:
1、順序性規(guī)則:對于線程 T1,value+=1 Happens-Before 釋放鎖的操作 unlock();
2、volatile 變量規(guī)則:由于 state = 1 會先讀取 state,所以線程 T1 的 unlock() 操作 Happens-Before 線程 T2 的 lock() 操作;
3、傳遞性規(guī)則:線程 T1 的 value+=1 Happens-Before 線程 T2 的 lock() 操作。
什么是可重入鎖
所謂可重入鎖,顧名思義,指的是線程可以重復(fù)獲取同一把鎖
除了可重入鎖,可能你還聽說過可重入函數(shù),可重入函數(shù)怎么理解呢?指的是線程可以重復(fù)調(diào)用?顯然不是,所謂可重入函數(shù),指的是多個線程可以同時調(diào)用該函數(shù),每個線程都能得到正確結(jié)果;同時在一個線程內(nèi)支持線程切換,無論被切換多少次,結(jié)果都是正確的。多線程可以同時執(zhí)行,還支持線程切換,這意味著什么呢?線程安全啊。所以,可重入函數(shù)是線程安全的。
class X {private final Lock rtl = new ReentrantLock();int value;public int get() {// 獲取鎖rtl.lock(); try {return value;} finally {// 保證鎖能釋放rtl.unlock();}}public void addOne() {// 獲取鎖rtl.lock(); try {value = 1 + get(); } finally {// 保證鎖能釋放rtl.unlock();}} }公平鎖與非公平鎖
在使用 ReentrantLock 的時候,你會發(fā)現(xiàn) ReentrantLock 這個類有兩個構(gòu)造函數(shù),一個是無參構(gòu)造函數(shù),一個是傳入 fair 參數(shù)的構(gòu)造函數(shù)。fair 參數(shù)代表的是鎖的公平策略,如果傳入 true 就表示需要構(gòu)造一個公平鎖,反之則表示要構(gòu)造一個非公平鎖
// 無參構(gòu)造函數(shù):默認(rèn)非公平鎖 public ReentrantLock() {sync = new NonfairSync(); } // 根據(jù)公平策略參數(shù)創(chuàng)建鎖 public ReentrantLock(boolean fair){sync = fair ? new FairSync() : new NonfairSync(); }鎖都對應(yīng)著一個等待隊列,如果一個線程沒有獲得鎖,就會進(jìn)入等待隊列,當(dāng)有線程釋放鎖的時候,就需要從等待隊列中喚醒一個等待的線程。如果是公平鎖,喚醒的策略就是誰等待的時間長,就喚醒誰,很公平;如果是非公平鎖,則不提供這個公平保證,有可能等待時間短的線程反而先被喚醒。
總結(jié)
以上是生活随笔為你收集整理的Java并发编程实战~Lock的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Data JPA 从入门到
- 下一篇: Effective Java~26. 不