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
9
import  java.util.HashMap;
import  java.util.concurrent.TimeUnit;
import  java.util.concurrent.locks.ReadWriteLock;
import  java.util.concurrent.locks.ReentrantReadWriteLock;
public  class  TestReadWriteLock {
     private  static  final  int  MAX_INDEX =  100 ;
     private  HashMap<Integer,Integer> mInitDataMap;
     private  ReadWriteLock mRWlock;
     private  volatile  boolean  isNonStop;
                                 
     public  void  start() {
         this .isNonStop =  true ;
     }
     public  void  stop() {
         this .isNonStop =  false ;
     }
     public  TestReadWriteLock(){
         init();
     }
     private  void  init() {
         mInitDataMap =  new  HashMap<Integer,Integer>(MAX_INDEX);
         mRWlock =  new  ReentrantReadWriteLock();
                                     
         for ( int  i= 0 ; i<MAX_INDEX; i++){
             mInitDataMap.put(i, i);
         }
     }
     private  class  Reader  implements  Runnable{
         @Override
         public  void  run() {
             while (isNonStop){
                 mRWlock.readLock().lock();
                 System.out.println(Thread.currentThread().getName() +  ": get the read lock." );
                                             
                 try {
                     mInitDataMap.get(( int )((MAX_INDEX- 1 )*Math.random()));
                                                 
                     //此处增加读取时间使写线程更容易处于饥饿状态
                     /*try {
                         TimeUnit.MILLISECONDS.sleep((long) (1000*Math.random()));
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }*/
                 }
                 finally{
                     mRWlock.readLock().unlock();
                 }
             }          
         }
                                     
     }
                                 
     private class Writer implements Runnable{
         @Override
         public void run() {
             while(isNonStop){
                 mRWlock.writeLock().lock();
                 System.out.println(Thread.currentThread().getName() + ": get the write lock.");
                                             
                 try{
                     mInitDataMap.put((int)((MAX_INDEX-1)*Math.random()), (int)((MAX_INDEX-1)*Math.random()));
                                                 
                     //此处增加读取时间使写线程更容易处于饥饿状态
                     /*try {
                         TimeUnit.MILLISECONDS.sleep((long) (1000));
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }*/
                 }
                 finally {
                     mRWlock.writeLock().unlock();
                 }
             }          
         }
                                     
     }
     public  static  void  main(String[] args)  throws  InterruptedException {
         TestReadWriteLock testReadWriteLock =  new  TestReadWriteLock();
         testReadWriteLock.start();
                                     
         for ( int  i= 0 ; i< 5 *MAX_INDEX; i++){
             new  Thread(testReadWriteLock. new  Reader()).start();
         }
                                     
         new  Thread(testReadWriteLock. new  Writer()).start();
                                     
         TimeUnit.SECONDS.sleep( 5 );
         testReadWriteLock.stop();
     }
}






     本文转自sarchitect 51CTO博客,原文链接:http://blog.51cto.com/stevex/1301216,如需转载请自行联系原作者



相关文章
|
6月前
|
Java 程序员 API
【Lock锁的使用与原理】
【Lock锁的使用与原理】
|
8月前
|
数据可视化 Java
lock锁和死锁
lock锁和死锁
46 0
|
9月前
|
Java
JUC基础(三)—— Lock锁 及 AQS(2)
JUC基础(三)—— Lock锁 及 AQS
64 0
|
9月前
|
算法 调度
JUC基础(三)—— Lock锁 及 AQS(1)
JUC基础(三)—— Lock锁 及 AQS
82 0
|
11月前
|
API
【JUC基础】04. Lock锁
java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类,说白了就是锁所在的包。
5487 0
|
安全 Java
多线程详解p18、Lock锁
多线程详解p18、Lock锁
|
缓存
ReadWriteLock可重入读写锁读写锁
ReadWriteLock可重入读写锁读写锁
120 0
|
缓存 Oracle 关系型数据库
可重入读写锁ReentrantReadWriteLock的使用详解
ReentrantReadWriteLock是一把可重入读写锁,这篇文章主要是从使用的角度帮你理解,希望对你有帮助。
157 0
可重入读写锁ReentrantReadWriteLock的使用详解
|
安全 Java 调度
多线程同步问题,锁Lock,synchronized
线程同步机制 并发:同一个对象被多个线程同时操作 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个县城再使用 线程同步形成条件:队列+