#yyds干货盘点# JUC锁: ReentrantReadWriteLock详解

Posted 灰太狼_cxh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# JUC锁: ReentrantReadWriteLock详解相关的知识,希望对你有一定的参考价值。

JUC锁: ReentrantReadWriteLock详解

ReentrantReadWriteLock数据结构

ReentrantReadWriteLock底层是基于ReentrantLock和AbstractQueuedSynchronizer来实现的,所以,ReentrantReadWriteLock的数据结构也依托于AQS的数据结构。

ReentrantReadWriteLock源码分析

类的继承关系

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable 

ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。

内部类 - Sync类

类的继承关系

abstract static class Sync extends AbstractQueuedSynchronizer 

说明: Sync抽象类继承自AQS抽象类,Sync类提供了对ReentrantReadWriteLock的支持。

类的内部类

Sync类内部存在两个内部类,分别为HoldCounter和ThreadLocalHoldCounter,其中HoldCounter主要与读锁配套使用,其中,HoldCounter源码如下。

// 计数器
static final class HoldCounter
// 计数
int count = 0;
// Use id, not reference, to avoid garbage retention
// 获取当前线程的TID属性的值
final long tid = getThreadId(Thread.currentThread());

说明: HoldCounter主要有两个属性,count和tid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程。ThreadLocalHoldCounter的源码如下

// 本地线程计数器
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter>
// 重写初始化方法,在没有进行set的情况下,获取的都是该HoldCounter值
public HoldCounter initialValue()
return new HoldCounter();

说明: ThreadLocalHoldCounter重写了ThreadLocal的initialValue方法,ThreadLocal类可以将线程与对象相关联。在没有进行set的情况下,get到的均是initialValue方法里面生成的那个HolderCounter对象。

类的属性

abstract static class Sync extends AbstractQueuedSynchronizer 
// 版本序列号
private static final long serialVersionUID = 6317671515068378041L;
// 高16位为读锁,低16位为写锁
static final int SHARED_SHIFT = 16;
// 读锁单位
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// 读锁最大数量
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
// 写锁最大数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 本地线程计数器
private transient ThreadLocalHoldCounter readHolds;
// 缓存的计数器
private transient HoldCounter cachedHoldCounter;
// 第一个读线程
private transient Thread firstReader = null;
// 第一个读线程的计数
private transient int firstReaderHoldCount;

说明: 该属性中包括了读锁、写锁线程的最大量。本地线程计数器等。

类的构造函数

// 构造函数
Sync()
// 本地线程计数器
readHolds = new ThreadLocalHoldCounter();
// 设置AQS的状态
setState(getState()); // ensures visibility of readHolds

说明: 在Sync的构造函数中设置了本地线程计数器和AQS的状态state。

内部类 - Sync核心函数分析

对ReentrantReadWriteLock对象的操作绝大多数都转发至Sync对象进行处理。下面对Sync类中的重点函数进行分析

sharedCount函数

表示占有读锁的线程数量,源码如下

static int sharedCount(int c)     return c >>> SHARED_SHIFT; 

说明: 直接将state右移16位,就可以得到读锁的线程数量,因为state的高16位表示读锁,对应的低十六位表示写锁数量。

exclusiveCount函数

表示占有写锁的线程数量,源码如下

static int exclusiveCount(int c)  return c & EXCLUSIVE_MASK; 

说明: 直接将状态state和(2^16 - 1)做与运算,其等效于将state模上2^16。写锁数量由state的低十六位表示。

tryRelease函数

/*
* Note that tryRelease and tryAcquire can be called by
* Conditions. So it is possible that their arguments contain
* both read and write holds that are all released during a
* condition wait and re-established in tryAcquire.
*/

protected final boolean tryRelease(int releases)
// 判断是否伪独占线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 计算释放资源后的写锁的数量
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0; // 是否释放成功
if (free)
setExclusiveOwnerThread(null); // 设置独占线程为空
setState(nextc); // 设置状态
return free;

​说明: 此函数用于释放写锁资源,首先会判断该线程是否为独占线程,若不为独占线程,则抛出异常,否则,计算释放资源后的写锁的数量,若为0,表示成功释放,资源不将被占用,否则,表示资源还被占用。​

以上是关于#yyds干货盘点# JUC锁: ReentrantReadWriteLock详解的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# JUC锁: LockSupport详解

#yyds干货盘点# JUC线程池: FutureTask详解

#yyds干货盘点#盘点MySQL的锁机制是如何实现的

16张图解锁Spring的整体脉络#yyds干货盘点#

#yyds干货盘点# Zookeeper实现分布式锁

#yyds干货盘点# 巧用Redis,实现分布式锁