[关闭]
@boothsun 2017-12-03T08:35:13.000000Z 字数 2225 阅读 1441

ReentrantReadWriteLock 可重入 读写锁(未完 写烦了 不想写了)

Java多线程


带着疑问去阅读

  1. ReentrantReadWriteLock 是如何实现读锁和写锁的?
  2. ReentrantReadWriteLock 是如何实现锁降级的?

ReentrantReadWriteLock简介

synchronized和ReentrantLock都是排他锁,这些锁在同一时刻只运行一个线程进行访问,而读写锁在同一时刻可以运行多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发现相比一般的排他锁有了很大的提升。

读写锁适合读多写少的场景。

ReentrantReadWriteLock的特性如下:

宏观看 ReentrantReadWriteLock

ReentrantReadWriteLock实现了ReadWriteLock接口,主要定义了获取读锁和写锁的方法,其内部主要有5个内部类,分别是Sync、NonfairSync、FairSync、ReadLock、WriteLock;Sync、NonfairSync、FairSync是继承自AQS,主要是负责锁状态的维护。ReadLock、WriteLock实现了Lock接口,主要定了获得锁和释放锁等基础操作,其最终的操作还是转换到Sync、NonfairSync、FairSync上。

ReentrantReadWriteLock 读写锁的实现分析

接下来分析ReentrantReadWriteLock的实现,主要包括:读写状态的设计、写锁的获取与释放、读锁的获取与释放以及锁降级。

读写状态的设计

我们知道,在AQS内部是以单个int类型的原子变量来表示锁状态的,AQS定义了4个抽象方法(tryAcquire(int)、tryRelease(int)、tryAcquireShared(int)、tryReleaseShared(int),前面两个方法用于独占/排他模式,后面两个用于共享模式)留给子类实现,用于自定义同步器的行为以实现特定的功能。

对于ReentrantLock,它是可重入的独占锁。内部的Sync类实现了tryAcquire(int)、tryRelease(int)方法,并用状态的值来表示重入次数,加锁或重入锁时状态加1,释放锁时状态减1,状态值等于0表示锁空闲。

对于CountDownLatch,它是一个关卡,在条件满足前阻塞所有等待线程,条件满足后允许所有线程通过。内部类Sync把状态初始化为大于0的某个值,当状态大于0时所有wait线程阻塞,每调用一次countDown方法就把状态值减1,减为0时运行所有线程通过。利用了AQS的共享模式。

现在,我们知道读写锁同样依赖自定义同步器来实现同步功能,要用AQS来实现 ReentrantReadWriteLock。

一点思考问题:

一点提示:

举个例子:
先来一张读写锁状态划分图:

当前同步状态表示一个线程已经获取了写锁,且重进入了两次,同时也连续获取了两次读锁。读写锁是如何迅速确定读和写各自的状态呢?答案是通过位运算。假设当前同步状态值为S,写状态等于S&0x0000FFFF(将高16位全部抹去),读状态等于S>>>16(无符号补0右移16位)。当写状态增加1时,等于S+1,当读状态增加1时,等于S+(1<<16),也就是S+0x00010000。

根据状态的划分能得出一个推论:S不等于0时,当写状态(S&0x0000FFFF)等于0时,则读状态(S>>>16)大于0,即读锁已被获取。

源码分析:

  1. abstract static class Sync extends AbstractQueuedSynchronizer {
  2. // 共享长度
  3. static final int SHARED_SHIFT = 16;
  4. // 由于读锁用高位部分,所以读锁个数加1,其实就是状态值加2^16
  5. static final int SHARED_UNIT = (1 << SHARED_SHIFT);
  6. // 写锁的可重入最大次数 读锁允许的最大数量
  7. static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
  8. // 写锁的掩码,用状态的低16位有效值
  9. static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
  10. // 读锁计数 当前持有读锁的线程数
  11. static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
  12. // 写锁的计数,也就是它的重入次数
  13. static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
  14. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注