不管是内部鎖還是Lock都是獨占鎖,或者稱之排他鎖,即讀寫互斥、寫寫互斥、讀讀互斥;與排他鎖相對的另一種鎖是共享鎖,Java的ReadWriteLock是一種共享鎖,提供讀讀共享,但讀寫和寫寫仍然互斥。
ReadWriteLock最大的特性就是讀讀共享,比如A線程讀鎖正在進行讀取操作,此時如果B線程請求讀鎖,那麼B線程可以馬上順利獲得讀鎖而無需等待,但此時如果C線程請求寫鎖,那麼C線程需要等待鎖可用。ReadWriteLock由于提供了讀讀共享而增加了複雜性,是以在讀寫都相當頻繁的場景并不能展現出性能優勢,隻有在讀操作極多而寫操作極少的場景下才能展現其性能優勢。比如,一個應用系統安裝完成後需要導入一批維護性的初始化資料,這些資料可以通過界面修改,但需要修改的情況極少,當系統一啟動就會自動加載初始化資料到指定資料結構(如HashMap)供各個子產品讀取使用,那麼可以為這些資料的讀寫加ReadWriteLock,以提高讀取性能并保持資料的一緻性。
ReentrantReadWriteLock類是ReadWriteLock接口的一個實作,它與ReentrantLock類一樣提供了公平競争與不公平競争兩種機制,預設也是使用非公平競争機制。ReentrantLock是排他鎖,使用非公平競争機制時,搶占的機會相對還是比較少的,隻有當新請求恰逢鎖釋放時才有機會搶占,是以發生線程饑餓的現象幾乎很少。然而ReentrantReadWriteLock是共享鎖,或者說讀讀共享,并且經常使用于讀多寫少的場景,即請求讀操作的線程多而頻繁而請求寫操作的線程極少且間隔長,在這種場景下,使用非公平競争機制極有可能造成寫線程饑餓。比如,R1線程此時持有讀鎖且在進行讀取操作,W1線程請求寫鎖是以需要排隊等候,在R1釋放鎖之前,如果R2,R3,...,Rn 不斷的到來請求讀鎖,因為讀讀共享,是以他們不用等待馬上可以獲得鎖,如此下去W1永遠無法獲得寫鎖,一直處于饑餓狀态。是以使用ReentrantReadWriteLock類時,小心選擇公平機制,以免遇到出乎預料的結果。
最後,Java5的讀寫鎖實作有瑕疵,可能發生死鎖,在Java6已經修複,是以避免使用Java5讀寫鎖。
示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<code>import</code> <code>java.util.HashMap;</code>
<code>import</code> <code>java.util.concurrent.TimeUnit;</code>
<code>import</code> <code>java.util.concurrent.locks.ReadWriteLock;</code>
<code>import</code> <code>java.util.concurrent.locks.ReentrantReadWriteLock;</code>
<code>public</code> <code>class</code> <code>TestReadWriteLock {</code>
<code> </code><code>private</code> <code>static</code> <code>final</code> <code>int</code> <code>MAX_INDEX = </code><code>100</code><code>;</code>
<code> </code><code>private</code> <code>HashMap<Integer,Integer> mInitDataMap;</code>
<code> </code><code>private</code> <code>ReadWriteLock mRWlock;</code>
<code> </code><code>private</code> <code>volatile</code> <code>boolean</code> <code>isNonStop;</code>
<code> </code>
<code> </code><code>public</code> <code>void</code> <code>start() {</code>
<code> </code><code>this</code><code>.isNonStop = </code><code>true</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>public</code> <code>void</code> <code>stop() {</code>
<code> </code><code>this</code><code>.isNonStop = </code><code>false</code><code>;</code>
<code> </code><code>public</code> <code>TestReadWriteLock(){</code>
<code> </code><code>init();</code>
<code> </code><code>private</code> <code>void</code> <code>init() {</code>
<code> </code><code>mInitDataMap = </code><code>new</code> <code>HashMap<Integer,Integer>(MAX_INDEX);</code>
<code> </code><code>mRWlock = </code><code>new</code> <code>ReentrantReadWriteLock();</code>
<code> </code>
<code> </code><code>for</code><code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<MAX_INDEX; i++){</code>
<code> </code><code>mInitDataMap.put(i, i);</code>
<code> </code><code>}</code>
<code> </code><code>private</code> <code>class</code> <code>Reader </code><code>implements</code> <code>Runnable{</code>
<code> </code><code>@Override</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code> </code><code>while</code><code>(isNonStop){</code>
<code> </code><code>mRWlock.readLock().lock();</code>
<code> </code><code>System.out.println(Thread.currentThread().getName() + </code><code>": get the read lock."</code><code>);</code>
<code> </code>
<code> </code><code>try</code><code>{</code>
<code> </code><code>mInitDataMap.get((</code><code>int</code><code>)((MAX_INDEX-</code><code>1</code><code>)*Math.random()));</code>
<code> </code>
<code> </code><code>//此處增加讀取時間使寫線程更容易處于饑餓狀态</code>
<code> </code><code>/*try {</code>
<code> </code><code>TimeUnit.MILLISECONDS.sleep((long) (1000*Math.random()));</code>
<code> </code><code>} catch (InterruptedException e) {</code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>}*/</code>
<code> </code><code>}</code>
<code> </code><code>finally{</code>
<code> </code><code>mRWlock.readLock().unlock();</code>
<code> </code><code>} </code>
<code> </code><code>private class Writer implements Runnable{</code>
<code> </code><code>public void run() {</code>
<code> </code><code>while(isNonStop){</code>
<code> </code><code>mRWlock.writeLock().lock();</code>
<code> </code><code>System.out.println(Thread.currentThread().getName() + ": get the write lock.");</code>
<code> </code><code>try{</code>
<code> </code><code>mInitDataMap.put((int)((MAX_INDEX-1)*Math.random()), (int)((MAX_INDEX-1)*Math.random()));</code>
<code> </code><code>TimeUnit.MILLISECONDS.sleep((long) (1000));</code>
<code> </code><code>finally</code><code>{</code>
<code> </code><code>mRWlock.writeLock().unlock();</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) </code><code>throws</code> <code>InterruptedException {</code>
<code> </code><code>TestReadWriteLock testReadWriteLock = </code><code>new</code> <code>TestReadWriteLock();</code>
<code> </code><code>testReadWriteLock.start();</code>
<code> </code><code>for</code><code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<</code><code>5</code><code>*MAX_INDEX; i++){</code>
<code> </code><code>new</code> <code>Thread(testReadWriteLock.</code><code>new</code> <code>Reader()).start();</code>
<code> </code><code>new</code> <code>Thread(testReadWriteLock.</code><code>new</code> <code>Writer()).start();</code>
<code> </code><code>TimeUnit.SECONDS.sleep(</code><code>5</code><code>);</code>
<code> </code><code>testReadWriteLock.stop();</code>
<code>}</code>
<code></code>
本文轉自sarchitect 51CTO部落格,原文連結:http://blog.51cto.com/stevex/1301216,如需轉載請自行聯系原作者