天天看点

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,如需转载请自行联系原作者