欢送来到《并发王者课》,本文是该系列文章中的第15篇

在上篇文章中,咱们介绍了Java中锁的根底Lock接口。在本文中,咱们将介绍Java中锁的另外一个重要的基本型接口,即ReadWriteLock接口。

在摸索Java中的并发时,ReadWriteLock无疑是重要的,然而了解它却并不容易。如果你此前已经检索材料,应该会发现大部分的文章对它的形容都比拟艰涩难懂,或连篇累牍的源码排列,或隔靴搔痒的喋喋不休,既说不到重点,也说不清前因后果。

所以,在本文中咱们会将介绍的重点放在对思路的了解上,而不是对源码的解读上。对于源码以及其背地的常识,咱们将在前面的更高级的系列中进行解说。

一、了解ReadWriteLock存在的价值

了解ReadWriteLock,首页要了解它存在的意义是什么。换言之,它要解决什么问题。为此,咱们无妨从下图着手一探到底。

不知你看明确了没有,这幅图所表白的有三层含意:

  • 大量线程在竞争同一份资源;
  • 这些线程中有的是读申请,有的是写申请
  • 在多个线程的申请中,读申请显著高于写申请

这样的场景是否似曾相识?没错,它就是典型的缓存利用场景

家喻户晓,缓存的存在是为了进步利用的读写性能。一方面,咱们须要通过缓存拦挡大量的读数据的申请。另一方面,咱们也须要不定期地更新缓存。但总体而言,更新缓存的次数远远小于读缓存的次数

在这个过程中,关键问题在于,为了保持数据一致性,咱们在读写缓存的时候,不能让读申请拿到脏数据,这就须要用到锁。然而,更要害的问题在于,尽管读写之间须要互斥,但读与读之间不能够互斥

总结来说,这个问题次要有上面这几个要点:

  • 数据容许多个线程同时读取,但只容许一个线程进行写入
  • 在读取数据的时候,不能够存在写操作或者写申请
  • 在写数据的时候,不能够存在读申请

如果你对此依然有些迷茫,那么上面这张图倡议你珍藏,这张图正是ReadWriteLock对问题的概述和它的解决方案,也是诠释ReadWriteLock最好的一幅图。

在你没有了解ReadWriteLock之前,你会感觉它非常艰涩且源码干燥。然而,一旦你了解它要解决的问题,以及它所提供的计划后,你会发现它的设计居然如此奇妙。它居然设计了两种截然不同的锁,其中一把正如咱们此前认知的那样是线程互斥的,而另一把锁居然能够为多个线程所共享!两把锁的完满配合,解决了并发读写的场景问题。

在豁然开朗后,所谓源码不过是队列与共享,它们是ReadWriteLock的一种实现形式,而不是阻挡你了解的绊脚石。

二、自主实现ReadWriteLock

在了解了ReadWriteLock背地的问题和它的解决思路之后,咱们就能够齐全抛开JDK中的源码本人实现一把读写锁。

public class ReadWriteLock{  private int readers       = 0;  private int writers       = 0;  private int writeRequests = 0;  public synchronized void lockRead() throws InterruptedException{    while(writers > 0 || writeRequests > 0){      wait();    }    readers++;  }  public synchronized void unlockRead(){    readers--;    notifyAll();  }  public synchronized void lockWrite() throws InterruptedException{    writeRequests++;    while(readers > 0 || writers > 0){      wait();    }    writeRequests--;    writers++;  }  public synchronized void unlockWrite() throws InterruptedException{    writers--;    notifyAll();  }}

在读锁lockRead()中,是不容许有写申请写操作的。如果有,那么读申请将进入期待。

而在lockWrite()中,同时不容许读申请和其余写操作的存在,此时只容许有一个写申请

以上就是读写锁简略的自主实现形式。当然,它是不欠缺的,只是根本的示例。它没有思考到根本的线程重入问题,真实情况也比它简单很多,但你了解它的意思就好。

三、Java中的ReadWriteLock是如何实现的

最初,咱们再来看JDK中的ReadWriteLock实现的一些基本思路。ReadWriteLock和咱们上篇所说的Lock接口以及其余类的根本关系如下图所示:

能够看到,JDK中的读写锁的实现是在ReentrantReadWriteLock这个类中。ReentrantReadWriteLock蕴含了两个外部类:ReadLock和WriteLock,而这两个类又实现了Lock接口。

读写锁的降级与降级

读写锁的降级与降级是ReentrantReadWriteLock中的一个重要知识点,也是高频的面试题。

从读锁到写锁,称之为锁的降级,反之为锁的降级。了解读写锁的降级和降级,最直观的形式是写代码验证。

代码片段1,先获取读锁,再获取写锁。

public class ReadWriteLockDemo {    public static void main(String[] args) {        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();        readWriteLock.readLock().lock();        System.out.println("曾经获取读锁...");        readWriteLock.writeLock().lock();        System.out.println("曾经获取写锁...");    }}

输入后果如下:

曾经获取读锁...

代码片段2,先获取写锁,再获取读锁:

public class ReadWriteLockDemo {    public static void main(String[] args) {        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();        readWriteLock.writeLock().lock();        System.out.println("曾经获取写锁...");        readWriteLock.readLock().lock();        System.out.println("曾经获取读锁...");    }}

输入后果如下:

曾经获取写锁...曾经获取读锁...Process finished with exit code 0

这样一来,后果曾经非常明了。ReentrantReadWriteLock反对锁的降级,但不反对锁的降级

读写锁中的公平性

在后面的文章中,咱们讲过线程饥饿的由来和结果,所以良好的并发工具类在设计时都会思考到公平性,ReentrantReadWriteLock也是如此。

在ReentrantReadWriteLock中,同时提供了偏心和非偏心两种模式,且默认为非偏心模式。从上面摘取的源码片段中,能够清晰地看到。

 public ReentrantReadWriteLock() {        this(false); }    /**   /** * Creates a new {@code ReentrantReadWriteLock} with * default (nonfair) ordering properties. */public ReentrantReadWriteLock() {  this(false);}/** * Creates a new {@code ReentrantReadWriteLock} with * the given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */public ReentrantReadWriteLock(boolean fair) {  sync = fair ? new FairSync() : new NonfairSync();  readerLock = new ReadLock(this);  writerLock = new WriteLock(this);}

小结

以上就是对于读写锁的全部内容。在本文中,咱们从缓存问题登程,接着从ReadWriteLock中寻找答案,以便能从更轻松的角度了解ReadWriteLock的前因后果。

了解ReadWriteLock的要害不在于对源码的分析,而在于对其思路的了解。

另外,咱们简略地介绍了ReentrantReadWriteLock中的一些要害知识点,但诸如其背地的AQS等并没有开展陈说。对此也不用焦急,咱们会在前面有具体的剖析介绍。

注释到此结束,祝贺你又上了一颗星✨

夫子的试炼

  • 尝试在示例代码中减少对读写线程的重入反对。

延长浏览与参考资料

  • 示例代码参考
  • 《并发王者课》纲要与更新进度总览

对于作者

关注公众号【技术八点半】,及时获取文章更新。传递有品质的技术文章,记录平凡人的成长故事,偶然也聊聊生存和现实。晚上8:30推送作者品质原创,早晨20:30推送行业深度好文。

如果本文对你有帮忙,欢送点赞关注监督,咱们一起从青铜到王者