1. 程式人生 > >Java併發程式設計(10)-顯式鎖和讀寫鎖的使用

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

文章目錄


更多關於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);
    }

}