简介

ReentrantReadWriteLock, 可重入读写锁,包括公平锁和非公平锁,相比较公平锁而言,非公平锁有更好的吞吐量,但可能会出现队列里的线程无限期地推迟一个或多个读线程或写线程的情况,因为后来的线程不必入队等待就可以竞争锁。

概述

读写锁,分为读锁(共享锁)和写锁(独占锁),有两种模式,包括公平模式和非公平模式。

非公平模式

读写锁的默认模式,竞争到锁的线程是无序的,因为,后来者可能先抢到线程,这大大增加了吞吐量。

对于写锁而言,如果当前线程具备获得锁的条件,则可以直接闯入获取锁。

对于读锁而言,如果排在等待队列head结点(最后一个已经获得锁的线程结点)后面的一个结点,等待的是写锁(isShared() == false),那么,当前线程应该入队等待;如果等待的是读锁,那么,当前线程可以闯入尝试获取锁。

公平模式

竞争到锁的线程是有序的(线程的到达顺序),即,对于写锁而言,一定是队列里等待最久的线程得到写锁;对读锁而言,一定是队列里等待最久的线程得到读锁。

锁降级

重入允许写锁降级为读锁。

具体是,线程先获取写锁,然后在获取读锁,最后释放写锁。

没有锁升级,即,从读锁升级为写锁是不允许的。

中断

读锁和写锁都支持锁获取期间的中断。

Condition

只能用于写锁。

应用

例一

 public class CachedData {
Object data; // 缓存的数据
volatile boolean cacheValid; // 缓存有效性
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); // 读写锁 void processCachedData() {
rwl.readLock().lock(); // 获取读锁
if (!cacheValid) { // 如果缓存有效,直接使用数据,否则,释放读锁,并获取写锁
rwl.readLock().unlock(); // 释放读锁
rwl.writeLock().lock(); // 获取读锁
try {
if (!cacheValid) { // 再次判断数据有效性,读锁模式下,其他线程也许已经更新了缓存数据
data = new Object(); // 更新缓存
cacheValid = true; // 设置为有效
}
rwl.readLock().lock(); // 获取读锁
} finally {
rwl.writeLock().unlock(); // 释放写锁
}
} try {
use(data); // 使用缓存
} finally {
rwl.readLock().unlock(); // 释放读锁
}
} private void use(Object data) { // 使用数据
System.out.println(data);
}
}

例二

 public class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>(); // 集合
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); // 读写锁
private final Lock r = rwl.readLock(); // 读锁
private final Lock w = rwl.writeLock(); // 写锁 public Data get(String key) { // 获取数据
r.lock(); // 获取读锁
try {
return m.get(key);
} finally {
r.unlock(); // 释放读锁
}
} public String[] allKeys() {
r.lock();
try {
return (String[]) m.keySet().toArray();
} finally {
r.unlock();
}
} public Data put(String key, Data value) { // 写数据
w.lock(); // 获取写锁
try {
return m.put(key, value);
} finally {
w.unlock(); // 释放写锁
}
} public void clear() {
w.lock();
try {
m.clear();
} finally {
w.unlock();
}
}
} class Data {
}

源码分析

属性

     private final ReentrantReadWriteLock.ReadLock readerLock; // 读锁
private final ReentrantReadWriteLock.WriteLock writerLock; // 写锁
final Sync sync; // 内部类,继承子AQS

构造方法

     public ReentrantReadWriteLock() { // 构造方法
this(false); // 默认非公平模式
} public ReentrantReadWriteLock(boolean fair) { // 构造方法,根据fair觉得公平或非公平模式
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}

获取读写锁

     public ReentrantReadWriteLock.WriteLock writeLock() {
return writerLock; // 写锁
} public ReentrantReadWriteLock.ReadLock readLock() {
return readerLock; // 读锁
}

内部类Sync

属性

         static final int SHARED_SHIFT = 16; // 高16位记录读锁的个数,低16位记录写锁的个数
static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 读锁的单位,读锁减1,相当于减此单位值,写锁的单位(1)
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 锁的最大个数
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 写锁掩码,计算其个数时,与之相与,使高位为0,只计算低位 static int sharedCount(int c) {// 计算读锁的个数
return c >>> SHARED_SHIFT;
} static int exclusiveCount(int c) { // 计算写锁的个数
return c & EXCLUSIVE_MASK;
} static final class HoldCounter { // Sync的内部类,当前线程所持锁的计数器
int count = 0; // 个数
final long tid = getThreadId(Thread.currentThread()); // 线程ID
} static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() { // 初始值
return new HoldCounter();
}
} private transient ThreadLocalHoldCounter readHolds; // 当前线程所持有的可重入读锁的计数器,减小到0时删除 private transient HoldCounter cachedHoldCounter; // 缓存成功获取读锁的最后一个线程的计数器 private transient Thread firstReader = null; // 第一个获得读锁的线程
private transient int firstReaderHoldCount; // 第一个获得读锁线程所持锁的个数

构造方法

         Sync() { // 构造方法
readHolds = new ThreadLocalHoldCounter();
setState(getState());
}

是否入队

         abstract boolean readerShouldBlock(); // 如果当前线程尝试获取读锁,是否让其等待

         abstract boolean writerShouldBlock(); // 如果当前线程尝试获取写锁,是否让其等待
NonfairSync
         final boolean writerShouldBlock() {
return false; // 非公平模式下,获取写锁的线程可以直接插队
} final boolean readerShouldBlock() { // 非公平模式下,如果队列中排在head结点的线程等待的是写锁,则,当前线程应该入队等待;如果是读锁,则可以插队
return apparentlyFirstQueuedIsExclusive();
}
FairSync
         final boolean writerShouldBlock() {
return hasQueuedPredecessors(); // 公平模式下,如果等待队列里有线程在等待,则,当前线程应该入队等待
} final boolean readerShouldBlock() {
return hasQueuedPredecessors(); // 公平模式下,如果等待队列里有线程在等待,则,当前线程应该入队等待
}

获取写锁

         protected final boolean tryAcquire(int acquires) { // 获取写锁
Thread current = Thread.currentThread(); // 当前线程
int c = getState(); // 获取状态
int w = exclusiveCount(c); // 计算写锁的状态
if (c != 0) { // 如果当前状态不为0,说明有线程已经获得了读锁或写锁
if (w == 0 || current != getExclusiveOwnerThread()) // 如果有别的线程获取的是读锁(w == 0), 则返回false; 或者是有别的线程获取的是写锁,则查看那个线程是不是就是当前线程,如果不是,也返回false
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT) // 到这一步,说明当前线程已经获取过写锁,这次是重入,如果超出最大值,抛出一个错误
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 设置新的状态
return true;
}
if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) // 如果当前状态为0,说明还没有线程获取过读锁或写锁;然而,如果需要其等待,而不是让其获取,则返回false;或者CAS设置状态失败(被其他线程抢先了),也返回false
return false;
setExclusiveOwnerThread(current); // 设置当前线程为独占线程
return true; // 返回
}

释放写锁

         protected final boolean tryRelease(int releases) { // 释放写锁
if (!isHeldExclusively()) // 如果当前线程没有持有锁,直接抛异常
throw new IllegalMonitorStateException();
int nextc = getState() - releases; // 计算新的状态
boolean free = exclusiveCount(nextc) == 0; // 计算写锁的空闲状态,如果没有线程持有写锁,表示空闲
if (free) // 如果是空闲状态
setExclusiveOwnerThread(null); // 独占者线程为null
setState(nextc); // 设置状态
return free; // 返回
}

获取读锁

         protected final int tryAcquireShared(int unused) { // 获取读锁
Thread current = Thread.currentThread(); // 获得当前线程
int c = getState(); // 获取当前状态
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) // 如果别的线程持有写锁,直接返回失败(-1)
return -1;
int r = sharedCount(c); // 获取读锁的个数
if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { // 同时满足后面3个条件 1.不应该等待 2.小于最大值 3.CAS设置状态成功
if (r == 0) { // 如果读锁个数为0
firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
firstReaderHoldCount = 1; // 个数设置为1
} else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
firstReaderHoldCount++; // 个数加1
} else {
HoldCounter rh = cachedHoldCounter; // 或取最后一个成功获取读锁的线程计数器
if (rh == null || rh.tid != getThreadId(current)) // 如果为空,或者当前线程不是最后一个成功获取读锁的线程
cachedHoldCounter = rh = readHolds.get(); // 则从ThreadLocal里获取一个(新的),并赋给缓存器
else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
readHolds.set(rh);
rh.count++; // 个数加1
}
return 1; // 返回成功(1)
}
return fullTryAcquireShared(current); // 最后,完整版本的尝试获取
}

完整版本的尝试获取读锁

         final int fullTryAcquireShared(Thread current) { // 完整版本的尝试获取读锁
HoldCounter rh = null; // 线程锁计数器
for (;;) {
int c = getState(); // 获取当前状态
if (exclusiveCount(c) != 0) { // 如果有线程持有写锁
if (getExclusiveOwnerThread() != current) // 判断持有写锁的线程是否是当前线程,如果不是,返回失败(-1)
return -1;
} else if (readerShouldBlock()) { // 如果应该等待
if (firstReader == current) {
} else { // 当前线程不是第一个获得读锁的线程
if (rh == null) { // 线程计数器为空
rh = cachedHoldCounter; // 获取缓存的,最后一次成功获得读锁的线程计数器
if (rh == null || rh.tid != getThreadId(current)) { // 为空,或者不是当前线程的计数器
rh = readHolds.get(); // 从线程变量里获取
if (rh.count == 0) // 如果个数为0
readHolds.remove(); // 从线程变量里删除
}
}
if (rh.count == 0) // 如果个数为0,返回失败(-1)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT) // 个数达到最大值,抛出一个错误
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // CAS设置新的状态
if (sharedCount(c) == 0) { // 如果读锁的个数为0
firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
firstReaderHoldCount = 1; // 个数设置为1
} else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
firstReaderHoldCount++; // 个数加1
} else {
if (rh == null)
rh = cachedHoldCounter; // 从缓存中获取计数器
if (rh == null || rh.tid != getThreadId(current)) // 为空,或者不是当前线程的计数器
rh = readHolds.get(); // 从线程变量里获取
else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
readHolds.set(rh);
rh.count++; // 个数加1
cachedHoldCounter = rh; // 缓存等待释放
}
return 1; // 返回成功(-1)
}
}
}

释放读锁

         protected final boolean tryReleaseShared(int unused) { // 释放读锁
Thread current = Thread.currentThread(); // 获得当前线程
if (firstReader == current) { // 如果当前线程是第一个获的读锁的线程
if (firstReaderHoldCount == 1) // 如果所持锁的个数为1
firstReader = null; // 置空
else
firstReaderHoldCount--; // 否则,递减
} else { // 如果不是
HoldCounter rh = cachedHoldCounter; // 获取最后成功获得读锁的计数器,缓存的目的是避免从ThreadLocal里取值
if (rh == null || rh.tid != getThreadId(current)) // 如果为空,或者不是当前线程的计数器,也即,当前线程不是成功获得读锁的线程
rh = readHolds.get(); // 获取当前线程的技术器
int count = rh.count; // 获取当前线程所持锁的个数
if (count <= 1) { // 如果个数小于或等于1
readHolds.remove(); // 删除当前线程的计数器
if (count <= 0) // 如果小于0,抛出异常
throw unmatchedUnlockException();
}
--rh.count; // 计数减1
}
for (;;) {
int c = getState(); // 获取当前状态
int nextc = c - SHARED_UNIT; // 计算新的状态
if (compareAndSetState(c, nextc)) // CAS设置新的状态
return nextc == 0; // 返回
}
}

尝试获取写锁

         final boolean tryWriteLock() { // 尝试获取写锁
Thread current = Thread.currentThread(); // 获取当前线程
int c = getState(); // 获取当前状态
if (c != 0) { // 如果有线程获取过锁,读锁或写锁
int w = exclusiveCount(c); // 计算写锁的个数
if (w == 0 || current != getExclusiveOwnerThread()) // 如果别的线程获取的是读锁,返回false; 或者有线程获取的是写锁,但那个线程不是当前线程,返回false
return false;
if (w == MAX_COUNT) // 超过最大值,抛出一个错误
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1)) // CAS 状态加1
return false;
setExclusiveOwnerThread(current); // 设置当前线程为独占者线程
return true;
}

尝试获取读锁

         final boolean tryReadLock() { // 尝试获取读锁
Thread current = Thread.currentThread(); // 获取当前线程
for (;;) {
int c = getState(); // 获取当前状态
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) // 如果有别的线程获取了写锁,直接返回失败
return false;
int r = sharedCount(c); // 获取读锁的个数
if (r == MAX_COUNT) // 达到最大值,抛出一个错误
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // CAS设置状态的值
if (r == 0) { // 如果读锁的个数为0
firstReader = current; // 当前线程设置为第一个成功获得读锁的线程
firstReaderHoldCount = 1; // 个数设置为1
} else if (firstReader == current) { // 或者当前线程是第一个成功获得读锁的线程
firstReaderHoldCount++; // 个数加1
} else {
HoldCounter rh = cachedHoldCounter; // 从缓存中获取计数器
if (rh == null || rh.tid != getThreadId(current)) // 为空,或者不是当前线程的计数器
cachedHoldCounter = rh = readHolds.get(); // 则从ThreadLocal里获取一个(新的),并赋给缓存器
else if (rh.count == 0) // 如果个数为0,则需要设置到线程变量里,因为release时,个数为0会删除
readHolds.set(rh);
rh.count++; // 个数加1
}
return true; // 返回
}
}
}

实现读写锁

例子来自网上。

 public final class ReadWriteLock {
private int readingReaders = 0; // 获得锁的读线程个数
private int writingWriters = 0; // 获得锁的写线程个数,最多1个
private int waitingWriters = 0; // 等待锁的写线程个数
private boolean preferWriter = true; // 偏向写线程,当写线程释放锁的时候,才为false public synchronized void readLock() throws InterruptedException { // 读锁
while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) { // 如果写线程获得锁,或者,等待锁的写线程个数大于0,而当前又偏向写线程的情况
wait(); // 则当前线程释放锁,进入等待队列,等待被唤醒,并重新抢占锁
}
readingReaders++; // 读线程个数加1
} public synchronized void readUnlock() { // 释放读锁
readingReaders--; // 读线程个数减1
preferWriter = true; // 偏向写线程
notifyAll(); // 唤醒所有等待的线程,包括读线程和写线程
} public synchronized void writeLock() throws InterruptedException { // 写锁
waitingWriters++; // 等待锁的写线程个数加1
try {
while (readingReaders > 0 || writingWriters > 0) { // 读锁或写锁个数大于0,则入队等待
wait();
}
} finally {
waitingWriters--; // 等待锁的写线程个数减1
}
writingWriters++; // 写线程个数加1
} public synchronized void writeUnlock() {
writingWriters--; // 线程个数减1
preferWriter = false; // 不偏向写线程
notifyAll(); // 唤醒所有等待的线程,包括读线程和写线程
}
}

行文至此结束。

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_rrwl.html

【JUC源码解析】ReentrantReadWriteLock的更多相关文章

  1. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  2. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

  3. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  4. 【JUC源码解析】DelayQueue

    简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...

  5. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

  6. 【JUC源码解析】ConcurrentLinkedQueue

    简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...

  7. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  8. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  9. Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer

    功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...

随机推荐

  1. BZOJ1499:[NOI2005]瑰丽华尔兹(DP,单调队列)

    Description 你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐.但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在 ...

  2. 【[SDOI2013]泉】

    \(hash\)+容斥 但是看到这个令人愉快的数据范围还是直接枚举子集吧 首先我们发现\(6\)这个东西简直是小的可怜,复杂度里肯定有\(2^6\)的 于是我们可以直接先枚举子集,把所有状态的对应相等 ...

  3. Django的模版引擎与模版使用

    Django的模版引擎与模版使用 模版引擎是模版响应的后端.模版指的是HTML.css,js等相关的文件.模版引擎是将这些表示层文件与数据相整合在一起,然后将整合后的数据给到响应类型判断采用一次性响应 ...

  4. Java50道经典习题-程序21 求阶乘

    题目:求1+2!+3!+...+20!的和分析:使用递归求解 0的阶乘和1的阶乘都为1 public class Prog21{ public static void main(String[] ar ...

  5. [Python 模块] logging模块、Logger类

    logging模块: 标准库里面的logging模块,在前面学习线程安全时曾用来解决print被打断的问题,这里会介绍logging模块的功能. logging模块是线程安全的,不需要客户做任何特殊的 ...

  6. python中matplotlib总结

    该总结只是为了记录自己学习过程中容易遗忘的问题,权当一个记事本使用. 1:散点图 plt.scatter()函数的原型 scatter(x, y, s=s, c=c, marker=marker, c ...

  7. vue项目中分享到朋友圈,调用微信接口

    虽然微信提供了jssdk,不代表可以点击按钮进行分享到朋友圈,是需要微信自带的浏览器右上角进行分享.手机浏览器需要浏览器支持分享到朋友圈的分享机制. 微信jssdk地址: https://mp.wei ...

  8. rman备份报错,全zero错误处理一例(bbed)

    问题:某数据库在执行rman全备的时候,发现alert日志中有报错,报错提示, file 10,block 305076全部为zero,内容全零,处理过程如下 分析处理: 1. 这个问题可能是 系统或 ...

  9. iOS:cocoapods 配置相关(19-04-02更)

    1.gem sources 2.libwebp 1.gem sources 因为,mac更新,cocoapods也要更新,使用下面指令,提示找不到.org,原因是淘宝的镜像源.org换成.com,所以 ...

  10. Python中级 —— 04网络编程

    网络编程 网络编程对所有开发语言都是一样的,Python也不例外.用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信. TCP编程 TCP建立可靠连 ...