1. 程式人生 > >java鎖機制,LOCK的實現類

java鎖機制,LOCK的實現類

1.synchronized

--把程式碼塊宣告為 synchronized,有兩個重要後果,通常是指該程式碼具有 原子性(atomicity)和 可見性(visibility)。
1.1 原子性
--原子性意味著個時刻,只有一個執行緒能夠執行一段程式碼,這段程式碼通過一個monitor object保護。從而防止多個執行緒在更新共享狀態時相互衝突。
1.2 可見性
--可見性則更為微妙,它要對付記憶體快取和編譯器優化的各種反常行為。它必須確保釋放鎖之前對共享資料做出的更改對於隨後獲得該鎖的另一個
執行緒是可見的 。

1.3 作用:如果沒有同步機制提供的這種可見性保證,執行緒看到的共享變數可能是修改前的值或不一致的值,這將引發許多嚴重問題。

2.synchronize的限制

synchronized是不錯,但它並不完美。它有一些功能性的限制:
--它無法中斷一個正在等候獲得鎖的執行緒;
--也無法通過投票得到鎖,如果不想等下去,也就沒法得到鎖;
--同步還要求鎖的釋放只能在與獲得鎖所在的堆疊幀相同的堆疊幀中進行,多數情況下,這沒問題(而且與異常處理互動得很好),但是,確實存在
一些非塊結構的鎖定更合適的情況。

3.LOCK的實現類

--Lock介面有三個實現類,一個是ReentrantLock,另兩個是ReentrantReadWriteLock類中的兩個靜態內部類ReadLock和WriteLock。
--與互斥鎖定相比,讀-寫鎖定允許對共享資料進行更高級別的併發訪問。雖然一次只有一個執行緒(writer 執行緒)可以修改共享資料,但在許多情況下,
任何數量的執行緒可以同時讀取共享資料(reader 執行緒)。從理論上講,與互斥鎖定相比,使用讀-寫鎖定所允許的併發性增強將帶來更大的效能提高。
--在實踐中,只有在多處理器上並且只在訪問模式適用於共享資料時,才能完全實現併發性增強。——例如,某個最初用資料填充並且之後不經常對其進行
修改的 collection,因為經常對其進行搜尋(比如搜尋某種目錄),所以這樣的 collection 是使用讀-寫鎖定的理想候選者。

4.ReentrantLock

原理 

可重入鎖的原理是在鎖內部維護了一個執行緒標示,標示該鎖目前被那個執行緒佔用,然後關聯一個計數器,一開始計數器值為0,說明該鎖沒有被任何執行緒佔用,
當一個執行緒獲取了該鎖,計數器會變成1,其他執行緒在獲取該鎖時候發現鎖的所有者不是自己所以被阻塞,
但是當獲取該鎖的執行緒再次獲取鎖時候發現鎖擁有者是自己會把計數器值+1, 當釋放鎖後計數器會-1,當計數器為0時候,鎖裡面的執行緒標示重置為null,這時 候阻塞的執行緒會獲取被喚醒來獲取該鎖.
--ReentrantLock 類實現了Lock ,它擁有與synchronized 相同的併發性和記憶體語義,但是添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。
此外,它還提供了在激烈爭用情況下更佳的效能。(換句話說,當許多執行緒都想訪問共享資源時,JVM 可以花更少的時候來排程執行緒,把更多時間
用在執行執行緒上。)

5.ReentrantLock擴充套件的功能

5.1 實現可輪詢的鎖請求

--在內部鎖中,死鎖是致命的——唯一的恢復方法是重新啟動程式,唯一的預防方法是在構建程式時不要出錯。而可輪詢的鎖獲取模式具有更完善的錯誤
恢復機制,可以規避死鎖的發生。 
--如果你不能獲得所有需要的鎖,那麼使用可輪詢的獲取方式使你能夠重新拿到控制權,它會釋放你已經獲得的這些鎖,然後再重新嘗試。
可輪詢的鎖獲取模式,由tryLock()方法實現。此方法僅在呼叫時鎖為空閒狀態才獲取該鎖。如果鎖可用,則獲取鎖,並立即返回值true。
如果鎖不可用,則此方法將立即返回值false。此方法的典型使用語句如下:
Lock lock = ...;   
if (lock.tryLock()) {   
try {   
// manipulate protected state   
} finally {   
lock.unlock();   
}   
} else {   
// perform alternative actions   
}  


5.2 實現可定時的鎖請求

--當使用內部鎖時,一旦開始請求,鎖就不能停止了,所以內部鎖給實現具有時限的活動帶來了風險。為了解決這一問題,可以使用定時鎖。
當具有時限的活動呼叫了阻塞方法,定時鎖能夠在時間預算內設定相應的超時。如果活動在期待的時間內沒能獲得結果,定時鎖能使程式提前返回。
可定時的鎖獲取模式,由tryLock(long, TimeUnit)方法實現。

5.3 實現可中斷的鎖獲取請求

--可中斷的鎖獲取操作允許在可取消的活動中使用。lockInterruptibly()方法能夠使你獲得鎖的時候響應中斷。

6.ReentrantLock不好與需要注意的地方

--lock 必須在 finally 塊中釋放。否則,如果受保護的程式碼將丟擲異常,鎖就有可能永遠得不到釋放!這一點區別看起來可能沒什麼,但是實際上,
它極為重要。忘記在 finally 塊中釋放鎖,可能會在程式中留下一個定時炸彈,當有一天炸彈爆炸時,您要花費很大力氣才有找到源頭在哪。
而使用同步,JVM 將確保鎖會獲得自動釋放.
--當 JVM 用 synchronized 管理鎖定請求和釋放時,JVM 在生成執行緒轉儲時能夠包括鎖定資訊。這些對除錯非常有價值,因為它們能標識死鎖或者
其他異常行為的來源。 Lock 類只是普通的類,JVM 不知道具體哪個執行緒擁有 Lock 物件。

7.執行緒間通訊Condition

--Condition可以替代傳統的執行緒間通訊,用await()替換wait(),用signal()替換notify(),用signalAll()替換notifyAll()。
--為什麼方法名不直接叫wait()/notify()/nofityAll()?因為Object的這幾個方法是final的,不可重寫!
--傳統執行緒的通訊方式,Condition都可以實現。 
Condition是被繫結到Lock上的,要建立一個Lock的Condition必須用newCondition()方法。 
Condition的強大在於它可以為多個執行緒間建立不同的Condition。