Java併發編程(10)-顯式鎖和讀寫鎖的使用


在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);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章