首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多執行緒基礎總結(八):ReentrantReadWriteLock 詳細解析

     編輯:Java 相關知識

說到 ReentrantReadWriteLock,首先要做的是與 ReentrantLock 劃清界限。它和後者都是單獨的實作,彼此之間沒有繼承或實作的關係。然後就是總結這個鎖機制的特性了:

  • (a) 重入方面: 其內部的 WriteLock 可以獲取 ReadLock,但是反過來 ReadLock 想要獲得 WriteLock 則永遠都不要想。
  • (b) 鎖降級: WriteLock 可以降級為 ReadLock,順序是:先獲得 WriteLock 再獲得 ReadLock,然後釋放 WriteLock,這時候執行緒將保持 ReadLock 的持有。反過來 ReadLock 想要升級為 WriteLock 則不可能,為什麼?參看 (a)。
  • (c) 讀寫特性: ReadLock 可以被多個執行緒持有並且在作用時排斥任何的 WriteLock,而 WriteLock 則是完全的互斥。這一特性最為重要,因為對於高讀取頻率而相對較低寫入的資料結構,使用此類鎖同步機制則可以提高併發量。
  • (d) 中斷支援: 不管是 ReadLock 還是 WriteLock 都支援 Interrupt,語義與 ReentrantLock 一致。
  • (e) Condition 支援: WriteLock 支援 Condition 並且與 ReentrantLock 語義一致,而 ReadLock 則不能使用 Condition,否則拋出 UnsupportedOperationException 異常。

以上就是比較重要的,或者衡量是否使用 ReentrantReadWriteLock 的基礎了。下面還是寫個小範例說明部分內容:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockSample {
    public static void main(String[] args) {
        testReadLock();
        // testWriteLock();
    }

    public static void testReadLock() {
        final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
        support.initCache();

        Runnable runnable = new Runnable() {
            public void run() {
                support.get("test");
            }
        };

        new Thread(runnable).start();
        new Thread(runnable).start();

        new Thread(new Runnable() {
            public void run() {
                support.put("test", "test");
            }
        }).start();
    }

    public static void testWriteLock() {
        final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
        support.initCache();

        new Thread(new Runnable() {
            public void run() {
                support.put("key1", "value1");
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                support.put("key2", "value2");
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                support.get("key1");
            }
        }).start();
    }
}

class ReadWriteLockSampleSupport {
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    private volatile boolean completed;
    private Map<String,String> cache;

    public void initCache() {
        readLock.lock();
        if(!completed) {
            // Must release read lock before acquiring write lock
            readLock.unlock(); // (1)
            writeLock.lock(); // (2)
            if(!completed) {
                cache = new HashMap<String,String>(32);
                completed = true;
            }
            // Downgrade by acquiring read lock before releasing write lock
            readLock.lock(); // (3)
            writeLock.unlock(); // (4) Unlock write, still hold read
        }

        System.out.println("empty? " + cache.isEmpty());
        readLock.unlock();
    }

    public String get(String key) {
        readLock.lock();
        System.out.println(Thread.currentThread().getName() + " read.");
        startTheCountdown();
        try{
            return cache.get(key);
        }
        finally{
            readLock.unlock();
        }
    }

    public String put(String key, String value) {
        writeLock.lock();
        System.out.println(Thread.currentThread().getName() + " write.");
        startTheCountdown();
        try{
            return cache.put(key, value);
        }
        finally {
            writeLock.unlock();
        }
    }

    /** A simple countdown, it will stop after about 5s. */
    public void startTheCountdown() {
        long currentTime = System.currentTimeMillis();
        for(;;) {
            long diff = System.currentTimeMillis() - currentTime;
            if(diff > 5000) {
                break;
            }
        }
    }
}

這個範例改造自 JDK 的 API 提供的示例,其中 ReadWriteLockSampleSupport 輔助類別負責維護一個 Map,當然前提是這個 Map 大部分的多執行緒下都是讀取,只有很少的比例是多執行緒競爭修改 Map 的值。其中的 initCache() 簡單的說明了特性 (a)、(b)。在這個方法中如果把註釋 (1) 和 (2) 處的程式碼調換位置,就會發現輕而易舉的死鎖了,當然是因為特性 (a) 的作用了。而註釋 (3)、(4) 處的程式碼位置則再次證明了特性 (a),並且有力的反映了特性 (b)——WriteLock 在 cache 初始化完畢之後,降級為 ReadLock。另外 get()、put() 方法在執行緒獲取鎖之後會在方法中停留近 5 秒的時間。

ReentrantReadWriteLockSample 中的兩個靜態測試方法則分別測試了 ReadLock 和 WriteLock 的排斥性。testReadLock() 中,開啟三個執行緒,前兩者試圖獲取 ReadLock 而後者去獲取 WriteLock。執行結果可以看到:ReadWriteLockSampleSupport 的 get() 方法中的列印結果在前兩個執行緒中幾乎同時顯示,而 put() 中的列印結果則要等上近 5 秒。這就說明了,ReadLock 可以多執行緒持有並且排斥 WriteLock 的持有執行緒。testWriteLock() 中,也開啟三個執行緒。前兩個是去獲取 WriteLock,最後一個獲取 ReadLock。執行的結果是三個列印結果都有近 5 秒的間隔時間,這說明了 WriteLock 是獨佔的,比較獨!

這篇 ReentrantReadWriteLock 的總結寫的有點遲了,主要是最近對 js 和 ajax 很有興趣,突然覺得 css 也很好玩。看著網上很多人對技術的狂熱和個人規劃,我想對我而言:不迷戀技術而是作為興趣,不管是 J2EE 還是 Web 前端,不管是移動設備的三方開發還是專業的視頻剪輯技術,我都希望很自然地感興趣了,有條件了就去狠狠地玩玩。我想我迷戀的只是高效能的計算機和互聯網,哈哈。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved