zoukankan      html  css  js  c++  java
  • ReentrantReadWriteLock(读写锁)

    ReentrantReadWriteLock是JDK5中提供的读写分离锁。读写分离锁可以有效的帮助减少锁的竞争,以此来提升系统的性能。用锁分离的机制来提升性能也非常好理解,比如线程A,B,C进行写操作,D,E,F进行读操作,如果使用ReentrantLock或者synchronized关键字,这些线程都是串行执行的,即每次都只有一个线程做操作。但是当D进行读操作时,E,F都需要等待锁,由于读不会对数据的完整性造成破坏,因此这种等待是不合理的。

      在这种情况下,读写锁运行多个线程同时读,是的D,E,F之间真正的并行。但是由于需要考虑数据的完整性,写写操作和读写操作还是需要互相等待和持有锁的,读写锁的约束情况如下:

        ❤ 读-读不互斥:读读之间不阻塞;

        ❤ 读-写互斥:读阻塞写,写也会阻塞读;

        ❤ 写-写互斥:写写阻塞;

    源码:

     这是ReentrantReadWriteLock的两个构造函数:

    1 public ReentrantReadWriteLock() {
    2         this(false);
    3     }
    4 
    5 public ReentrantReadWriteLock(boolean fair) {
    6     sync = fair ? new FairSync() : new NonfairSync();
    7     readerLock = new ReadLock(this);
    8     writerLock = new WriteLock(this);
    9   }

      可以看出,读写锁也可以构造公平和非公平锁,默认的是非公平锁。

    看下面的例子:

     1 public class ReadWriteLock {
     2     private static Lock lock = new ReentrantLock();
     3     private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
     4     private static Lock readLock = readWriteLock.readLock();
     5     private static Lock writeLock = readWriteLock.writeLock();
     6     private int value;
     7 
     8     //模拟读操作
     9     public Object handleRead(Lock lock) throws InterruptedException{
    10         try {
    11             lock.lock();
    12             System.out.println("获取读锁 :" + System.currentTimeMillis());
    13             Thread.sleep(1000);
    14             return value;
    15         } finally {
    16             lock.unlock();
    17         }
    18     }
    19 
    20     //模拟写操作
    21     public void handleWrite(Lock lock,int index) throws InterruptedException {
    22         try {
    23             lock.lock();
    24             System.out.println("获取写锁:" + System.currentTimeMillis());
    25             Thread.sleep(1000);
    26             value = index;
    27         }finally {
    28             lock.unlock();
    29         }
    30     }
    31 
    32     //测试
    33     public static void main(String[] args){
    34         ReadWriteLock demo = new ReadWriteLock();
    35         //读线程
    36         Runnable readRunnable = new Runnable() {
    37             @Override
    38             public void run() {
    39                 try {
    40                     demo.handleRead(readLock);
    41                     //demo.handleRead(lock);
    42                 } catch (InterruptedException e) {
    43                     e.printStackTrace();
    44                 }
    45             }
    46         };
    47         //写线程
    48         Runnable writeRunnable = new Runnable() {
    49             @Override
    50             public void run() {
    51                 try {
    52                     demo.handleWrite(writeLock,new Random().nextInt());
    53                     //demo.handleWrite(lock,new Random().nextInt());
    54                 } catch (InterruptedException e) {
    55                     e.printStackTrace();
    56                 }
    57             }
    58         };
    59 
    60         for (int i = 0;i < 10;i++){
    61             new Thread(readRunnable).start();
    62         }
    63 
    64         for (int i = 0;i < 10;i++){
    65             new Thread(writeRunnable).start();
    66         }
    67 
    68     }
    69 
    70 }

     输出结果:

     1 获取读锁 :1537857549893
     2 获取读锁 :1537857549893
     3 获取读锁 :1537857549893
     4 获取读锁 :1537857549893
     5 获取读锁 :1537857549893
     6 获取读锁 :1537857549893
     7 获取写锁:1537857550893
     8 获取写锁:1537857551893
     9 获取写锁:1537857552893
    10 获取写锁:1537857553893
    11 获取读锁 :1537857554893
    12 获取读锁 :1537857554893
    13 获取读锁 :1537857554893
    14 获取读锁 :1537857554893
    15 获取写锁:1537857555893
    16 获取写锁:1537857556893
    17 获取写锁:1537857557893
    18 获取写锁:1537857558893
    19 获取写锁:1537857559893
    20 获取写锁:1537857560893

       上述的我们分别让读和写的线程都等待1S,当我们使用读写锁时,可以有输出时间的时间戳看出,整个的过程耗时11秒;也可以从获得读锁的时间戳看出,获取读锁是可以同时获得的,表明读-读不互斥,可以并行的;看输出结果的第6,7行和10,11行和14,15行,可以看出这些获得锁的时间差都为1S,表明了读-写互斥和写-读互斥;再看获取写锁的时间戳,每次获得写锁都是互斥的,每次都间隔1S得到。

      将上述例子,第41和53行代码放开,注释第40和52行代码,执行得到结果:

     1 获取读锁 :1537859337210
     2 获取读锁 :1537859338210
     3 获取读锁 :1537859339210
     4 获取读锁 :1537859340210
     5 获取读锁 :1537859341210
     6 获取读锁 :1537859342210
     7 获取读锁 :1537859343210
     8 获取读锁 :1537859344210
     9 获取读锁 :1537859345210
    10 获取读锁 :1537859346210
    11 获取写锁:1537859347210
    12 获取写锁:1537859348210
    13 获取写锁:1537859349210
    14 获取写锁:1537859350210
    15 获取写锁:1537859351211
    16 获取写锁:1537859352211
    17 获取写锁:1537859353211
    18 获取写锁:1537859354211
    19 获取写锁:1537859355211
    20 获取写锁:1537859356211

     修改上述代码后,我们是使用重入锁来实现的,可以看出整个过程耗时20秒,再看每次获取锁的时间戳,我们可以得出不论什么线程都是互斥的。

      由两个输出结果可以看出,读写锁对于读比较多的应用场景性能提升较大。

    参考:《Java高并发程序设计》 葛一鸣 郭超 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    scss使用指南--每天一点
    Egret引擎开发基础(一)
    vue-awesome-swiper使用自动轮播和循环轮播不生效(loop和autoplay)
    Vue项目中title的设置,使用document.title返回时不生效
    charles系列破解激活注册码
    Vscode中vue项目中无法对js进行提示
    JS数组与对象的遍历方法大全
    手把手教你用webpack3搭建react项目(开发环境和生产环境)(二)
    服务器安装redis-stat和easyNmon
    Shell备份数据库
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/9699752.html
Copyright © 2011-2022 走看看