天天看點

Lock應用之 讀寫鎖

不管是内部鎖還是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&lt;Integer,Integer&gt; 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&lt;Integer,Integer&gt;(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&lt;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&lt;</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,如需轉載請自行聯系原作者