鎖的原理:任何時間都只能有一個執行緒持有鎖,只有持有鎖的執行緒才能訪問被鎖保護的資源,
我們接下來看一下在鎖的使用上有什么最佳實踐,
避免濫用鎖
如果能不用鎖,就不用鎖;如果你不確定是不是應該用鎖,那也不要鎖,
使用鎖后帶來的代價:
- 加鎖和解鎖程序都需要CPU時間的,這是一個性能的損失,使用鎖還可能導致執行緒等待鎖,等待鎖程序中的執行緒是阻塞狀態,過多的鎖等待會顯著降低程式的性能,
- 如果對鎖使用不當,很容易造成死鎖,導致整個程式“卡死”,這是非常嚴重的問題,
我們不可以看到一個共享資料,在沒有搞清楚它在并發環境中是否會出現爭用問題,就“為了保險,給它加個鎖吧,”,千萬不要有這種不負責任的想法,否則你將會付出慘痛的代價,
只有在并發環境中,共享資源不支持并發訪問,或者說并發訪問共享資源會導致系統錯誤的情況下,才需要使用鎖,
鎖的用法
使用鎖的程序可以分為三步:
- 在訪問共享資源之前,先獲取鎖,
- 如果獲取鎖成功,就可以訪問共享資源了,
- 使用完共享資源后釋放鎖,以便其他執行緒繼續訪問共享資源,
我們在使用鎖的程序中,需要注意使用完鎖,一定要釋放它,我們需要考慮到代碼可能走到的所有正常和例外的分支,確保所有情況下,鎖都能被釋放,
死鎖
死鎖是指由于某種原因,鎖一直沒有釋放,后續需要獲取鎖的執行緒都將處于等解鎖狀態,
大部分編程語言都提供了可重入鎖,如果沒有特別的需求,我們也要盡量使用可重入鎖,
下面是幾條如何避免死鎖的建議:
- 避免濫用鎖,
- 對于同一把鎖,加鎖和解鎖必須要放在同一個方法中,這樣一次加鎖對應一次解鎖,代碼清晰簡單,便于分析問題,
- 盡量避免在持有一把鎖的情況下,去獲取另外一把鎖,就是要盡量避免同時持有多把鎖,
- 如果需要持有多把鎖,一定要注意加解鎖的順序,解鎖的順序要和加鎖的殊勛想法,比如,獲取三把鎖的順序是A、B、C,釋放鎖的順序必須是C、B、A,
使用讀寫鎖兼顧性能和安全
對于共享資料,如果我們的方法只是去讀取它,而不會修改,也是需要加鎖的,因為有可能在讀取資料的程序中,有其他執行緒會更新資料,
但如果只是簡單地為資料加一個鎖,對于“讀多寫少”的場景,性能會受到影響,針對資料的讀寫操作,我們希望能夠做到:1)讀操作可以并發執行,2)寫的同時不能并發讀,也不能并發寫,
Java中的ReadWriteLock可以用來解決這個問題,看下面的代碼框架:
ReadWriteLock rwlock = new ReentrantReadWriteLock();
public void read() {
rwlock.readLock().lock();
try {
// 在這兒讀取共享資料
} finally {
rwlock.readLock().unlock();
}
}
public void write() {
rwlock.writeLock().lock();
try {
// 在這兒更新共享資料
} finally {
rwlock.writeLock().unlock();
}
}
在這段代碼中,需要讀資料的時候,我們獲取鎖,這個鎖不是一個互斥鎖,即read()方法可以支持多個執行緒并行執行,從而保證資料的讀性能,寫資料的時候,我們獲得寫鎖,這是一個互斥鎖,當一個執行緒持有寫鎖的時候,其他執行緒既無法獲得讀鎖,也無法獲得寫鎖,從而達到了保護資料的目的,
作者:李潘 出處:http://wing011203.cnblogs.com/ 本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利,轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/547330.html
標籤:其他
上一篇:怎么使用硬體同步原語替代鎖?
下一篇:怎么使用硬體同步原語替代鎖?
