說到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).WriteLock支持Condition並且與ReentrantLock語義一致,而ReadLock則不能使用Condition,否則拋出 UnsupportedOperationException異常。
以上就是比較重要的,或者衡量是否使用ReentrantReadWriteLock的基礎了。下面還是寫個小例子說明部分內容:
Java代碼
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author: yanxuxin
* @date: 2010-1-7
*/
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) 處的代碼調換位置,就會發現輕而易舉的死鎖了,當然是因為特性(1)的作用了。而注釋(3),(4)處的代碼位置則再次證明了特性 (a),並 且有力的反映了特性(b)--WriteLock在cache初始化完畢之後,降級為ReadLock。另外get(),put()方法在線程獲取鎖之後會在方法中呆上近 5s的時間。
ReentrantReadWriteLockSample中的兩個靜態測試方法則分別測試了ReadLock和WriteLock的排斥性。testReadLock()中,開啟三個線程 ,前兩者試圖獲取ReadLock而後者去獲取WriteLock。執行結果可以看到:ReadWriteLockSampleSupport的get()方法中的打印結果在前兩個 線程中幾乎同時顯示,而put()中的打印結果則要等上近5s。這就說明了,ReadLock可以多線程持有並且排斥WriteLock的持有線程。 testWriteLock()中,也開啟三個線程。前兩個是去獲取WriteLock,最後一個獲取ReadLock。執行的結果是三個打印結果都有近5s的間隔時 間,這說明了WriteLock是獨占的,比較獨!
這篇ReentrantReadWriteLock的總結寫的有點遲了,主要是最近對js和ajax很有興趣,突然覺得css也很好玩。看著網上很多人對技術的 狂熱和個人規劃,我想對我而言:不迷戀技術而是作為興趣,不管是J2EE還是Web前端,不管是移動設備的三方開發還是專業的視頻剪輯技 術,我都希望很自然的感興趣了,有條件了就去狠狠的玩玩。我想我迷戀的只是高性能的計算機和互聯網,哈哈。