Java併發程式設計(10)-顯式鎖和讀寫鎖的使用
阿新 • • 發佈:2018-11-15
文章目錄
更多關於Java併發程式設計的文章請點選這裡:Java併發程式設計實踐(0)-目錄頁
在Java5.0之前,用於調節共享物件訪問的機制只有synchronized和volatile。Java5.0之後提供了新的選擇:ReentrantLock,即顯式鎖
。顯式鎖與之前提過的synchronized的同步互斥 機制不太一樣,ReentrantLock並不作為內部鎖機制的替代,而是當內部鎖機制有侷限時可供選擇的高階特性。
本文總結自《Java併發程式設計實踐》 第十三章 顯式鎖 。
一、顯式鎖
1.1、什麼是顯式鎖
在Java5.0之前,用於調節共享物件訪問的機制只有synchronized和volatile。Java5.0之後提供了新的選擇:ReentrantLock,即顯式鎖。顯式鎖與之前提過的synchronized的同步互斥 機制不太一樣,ReentrantLock並不作為內部鎖機制的替代,而是當內部鎖機制有侷限時可供選擇的高階特性:
- 1、顯式鎖可供開發者人工調控,不易出現同步鎖的死鎖問題
- 2、顯式鎖是可輪詢的、定時的以及可中斷的鎖獲取操作、非快結構的加鎖(對應於synchronized的加鎖和釋放都是在同一塊程式碼塊中)
1.2、Lock和ReentrantLock
- Lock介面:
Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;//可中斷鎖(在獲取鎖的時候可以響應中斷)
boolean tryLock(); //輪詢鎖
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//定時鎖
void unlock(); //釋放鎖
Condition newCondition();
}
- ReentrantLock類:
ReentrantLock 是Lock的一個實現類,將由最近成功獲得鎖,並且還沒有釋放該鎖的執行緒所擁有。此類的構造方法接受一個可選的公平 引數。當設定為 true 時,在多個執行緒的爭用下,這些鎖傾向於將訪問權授予等待時間最長的執行緒。否則此鎖將無法保證任何特定訪問順序。
1.3、如何使用顯示鎖
首先思考一下,為什麼要使用顯式鎖呢?瞭解過synchronized同步鎖的朋友應該明白,當A執行緒持有該鎖,並且遲遲不能完成任務時,該鎖將一直被A持有且不能釋放,那麼B執行緒就需要等待A釋放鎖,這會形成一個問題,即執行緒飢餓死鎖。如以下程式碼:
public synchronized void function(){
//doing something...
}
但是,如果使用顯式鎖的話,我們可以人為地調控何時去獲得鎖,何時去釋放鎖,從而避免了死鎖的發生,這就是使用顯式鎖的原因。
Lock lock = new ReentrantLock();
lock.lock();//由執行到該處的執行緒獲得鎖
try{
//doing something
//捕獲異常進行處理
}finally {
lock.unlock();//釋放鎖
}
二、讀寫鎖
2.1、為什麼使用讀寫鎖
ReentrantLock是一種標準的互斥鎖,最多隻有一個執行緒能擁有它;這樣的實現是執行緒安全的,但是對讀和寫兩種情況都進行了同步限制,那麼在頻繁讀取時,會對效能造成不必要的浪費。所以,讀寫鎖的出現緩解了這樣的問題。讀寫鎖(ReadWriteLock)的加鎖策略允許多個同時存在的讀者,但是隻允許一個寫者。
2.2、ReadWriteLock介面和ReentrantReadWriteLock實現類
特點允許多個執行緒同時讀,允許一個執行緒寫。
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
//ReentrantReadWriteLock是預設的非公平讀寫鎖實現
ReentrantLock lock= new ReentrantReadWriteLock();
Lock readLock = lock.readLock();//獲得讀鎖
Lock writeLock = lock.writeLock();//獲得寫鎖
2.3、使用讀寫鎖
public class ReadWriteMap<K,V> {
//讀寫鎖
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//read lock
private Lock readLock = readWriteLock.readLock();
//write lock
private Lock writeLock = readWriteLock.writeLock();
//map
private final Map<K,V> map ;
//含參構造
public ReadWriteMap(Map map){
this.map = map;
}
//get方法,使用讀鎖
public V get(K key){
readLock.lock();//其他執行緒只讀不寫
return map.get(key);
}
//put方法,使用寫鎖
public void put(K key,V value){
writeLock.lock();//其他執行緒都不能寫
map.put(key,value);
}
}