文章目錄
在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);
}
}