Java并发JUC中ReentrantReadWriteLock源码剖析小记

Java并发领域中ReentrantReadWriteLock源码深度解析概览

目录

  • 1. ReentrantReadWriteLock是什么
  • 2. 非公平ReentrantReadWriteLock
    • 2.1. 特性阐述
    • 2.2. 使用方式
    • 2.3. 源码剖析
    • 2.3.1. uml图相关
    • 2.3.2. 构造函数细节
    • 2.3.3. 读锁加锁流程
      • 2.3.3.1. 借助AQS添加共享锁
      • 2.3.3.1.1. 利用Sync尝试添加共享锁
        • 2.3.3.1.1.1. 判定读操作是否需阻塞(非公平场景)
        • 2.3.3.1.1.2. 快速加锁失败时的死循环加锁处理
      • 2.3.3.1.2. 加锁失败后的AQS队列入队与阻塞
        • 2.3.3.1.2.1. 加入AQS队列并阻塞
    • 2.3.4. 读锁解锁流程
      • 2.3.4.1. 借助AQS释放共享锁
      • 2.3.4.1.1. 利用Sync尝试释放锁
      • 2.3.4.1.2. 所有共享锁释放完毕后唤醒AQS队列头节点后续节点
        • 2.3.4.1.2.1. 唤醒AQS队列头节点的下一个节点
    • 2.3.5. 写锁加锁流程
      • 2.3.5.1. 调用AQS添加互斥锁
      • 2.3.5.1.1. 利用Sync尝试添加互斥锁
        • 2.3.5.1.1.1. 判定写操作是否需阻塞(非公平场景)
      • 2.3.5.1.2. 加锁失败后的AQS队列入队与阻塞
    • 2.3.6. 写锁解锁流程
      • 2.3.6.1. 调用AQS释放互斥锁
      • 2.3.6.1.1. 调用Sync尝试释放互斥锁
      • 2.3.6.1.2. 尝试解锁成功后唤醒AQS队列头节点后续节点
  • 3. 公平ReentrantReadWriteLock
    • 3.1. 特性说明
    • 3.2. 使用示例
    • 3.3. 源码剖析
    • 3.3.1. uml相关内容
    • 3.3.2. 构造函数详情
    • 3.3.3. 读锁加锁流程
      • 3.3.3.1. 借助AQS添加共享锁
      • 3.3.3.1.1. 利用Sync尝试添加共享锁
        • 3.3.3.1.1.1. 判定读操作是否需阻塞(公平场景)
        • 3.3.3.1.1.2. 快速加锁失败时的死循环加锁处理
      • 3.3.3.1.2. 加锁失败后的AQS队列入队与阻塞
        • 3.3.3.1.2.1. 加入AQS队列并阻塞
    • 3.3.4. 读锁解锁流程
      • 3.3.4.1. 借助AQS释放共享锁
      • 3.3.4.1.1. 利用Sync尝试释放锁
      • 3.3.4.1.2. 所有共享锁释放完毕后唤醒AQS队列头节点后续节点
        • 3.3.4.1.2.1. 唤醒AQS队列头节点的下一个节点
    • 3.3.5. 写锁加锁流程
      • 3.3.5.1. 调用AQS添加互斥锁
      • 3.3.5.1.1. 利用Sync尝试添加互斥锁
        • 3.3.5.1.1.1. 判定写操作是否需阻塞(公平场景)
      • 3.3.5.1.2. 加锁失败后的AQS队列入队与阻塞
    • 3.3.6. 写锁解锁流程
      • 3.3.6.1. 调用AQS释放互斥锁
      • 3.3.6.1.1. 调用Sync尝试释放互斥锁
      • 3.3.6.1.2. 尝试解锁成功后唤醒AQS队列头节点后续节点
  • 4. 参考

1. ReentrantReadWriteLock是什么

ReentrantLock能确保同一时刻仅有一个线程可在临界区进行读或写操作,这意味着即便有两个读线程同时读取数据,ReentrantLock也只允许其中一个通过。然而,我们期望的是读操作能够并发执行,一旦有写操作,其他线程就需等待。具体情况如下表所示:

是否可以同时进行
×
× ×

基于此需求,ReentrantReadWriteLock应运而生。

2. 非公平ReentrantReadWriteLock

2.1. 特性阐述

在非公平模式下,不管队列前面是否有线程在排队等待获取锁,线程都会直接去尝试抢占锁。

2.2. 使用方式

以下是在非公平模式下的使用示例代码:

public class ReadWriteLockTest
{
    private static ReadWriteLock lock = new ReentrantReadWriteLock();//默认是非公平模式
    private static Lock readLock = lock.readLock();
    private static Lock writeLock = lock.writeLock();

    private static List<Integer> data = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException
    {
        Thread readThread = new Thread(() -> {
            while (true)
            {
                try
                {
                    TimeUnit.MILLISECONDS.sleep(500);
                    readLock.lock();
                    System.out.println(Thread.currentThread().getName() + " read: " + data);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    readLock.unlock();
                }
            }
        });
        Thread readThread2 = new Thread(() -> {
            while (true)
            {
                try
                {
                    TimeUnit.MILLISECONDS.sleep(300);

                    readLock.lock();
                    System.out.println(Thread.currentThread().getName() + " read: " + data);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    readLock.unlock();
                }
            }
        });
        Thread writeThread = new Thread(() -> {

            int i = 0;
            while (true)
            {
                try
                {
                    TimeUnit.MILLISECONDS.sleep(200);

                    writeLock.lock();
                    if (i % 2 == 0)
                    {
                        data.add(i);
                    }else
                    {
                        data.remove(0);
                    }
                    i++;
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    writeLock.unlock();
                }
            }
        });

        readThread.start();
        readThread2.start();
        writeThread.start();

        readThread.join();
        readThread2.join();
        writeThread.join();

    }

}

2.3. 源码剖析

2.3.1. uml图相关

以下是相关的uml图示意:

@startuml
skinparam classAttributeIconSize 0

interface AQS{
}

class Sync{
}

interface Lock{
}

interface ReadWriteLock{
}

class ReentrantReadWriteLock{
}

class WriteLock{
}

class ReadLock{
}

class WriteLock{

}

Lock <|-- ReadLock

Lock <|-- WriteLock

ReadWriteLock <|-- ReentrantReadWriteLock
AQS <|-- Sync

ReentrantReadWriteLock --> ReadLock
ReentrantReadWriteLock --> WriteLock
ReadLock --> Sync
WriteLock --> Sync
@enduml

2.3.2. 构造函数细节

  • ReentrantReadWriteLock构造函数
public ReentrantReadWriteLock() {
    // 默认使用非公平模式
    this(false);
}

public ReentrantReadWriteLock(boolean fair) {
    // 根据传入的fair参数初始化Sync,若为false则使用NonfairSync
    sync = fair ? new FairSync() : new NonfairSync();
    // 初始化读写锁
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}
  • ReentrantReadWriteLock.ReadLock构造函数
protected ReadLock(ReentrantReadWriteLock lock) {
    // 保存ReentrantReadWriteLock的Sync
    sync = lock.sync;
}
  • ReentrantReadWriteLock.WriteLock构造函数
protected WriteLock(ReentrantReadWriteLock lock) {
    // 保存ReentrantReadWriteLock的Sync
    sync = lock.sync;
}

2.3.3. 读锁加锁流程

  • ReentrantReadWriteLock.readLock方法
// 返回读锁实例
public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
  • ReadLock.lock方法
public void lock() {
    // 调用AQS添加共享锁
    sync.acquireShared(1);
}
2.3.3.1. 借助AQS添加共享锁
  • AQS acquireShared方法
public final void acquireShared(int arg) {
    // 调用ReentrantReadWriteLock的Sync的tryAcquireShared方法
    // 如果返回值小于0,说明加锁失败,需要执行doAcquireShared入队并阻塞
    // 如果返回值大于等于0,说明加锁成功,执行后续逻辑
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
2.3.3.1.1. 利用Sync尝试添加共享锁
  • ReentrantReadWriteLock.Sync.tryAcquireShared方法
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    // 如果写锁数量不为0且加锁线程不是当前线程,返回-1表示加锁失败
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;

    int r = sharedCount(c);
    // 非公平锁下判断读操作是否需阻塞
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        // 处理读锁相关的状态和计数
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    // 快速加锁失败时的处理
    return fullTryAcquireShared(current);
}
2.3.3.1.1.1. 判定读操作是否需阻塞(非公平场景)
  • ReentrantReadWriteLock.NonfairSync.readerShouldBlock方法
final boolean readerShouldBlock() {
    // 非公平模式下,若队列头节点是互斥节点(加写锁的节点)则需阻塞读操作
    return apparentlyFirstQueuedIsExclusive();
}

final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}
2.3.3.1.1.2. 快速加锁失败时的死循环加锁处理
  • ReentrantReadWriteLock.Sync.fullTryAcquireShared方法
final int fullTryAcquireShared(Thread current) {
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                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)
                            readHolds.remove();
                    }
                }
                if (rh.count == 0)
                    return -1;
            }
        }
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh;
            }
            return 1;
        }
    }
}
2.3.3.1.2. 加锁失败后的AQS队列入队与阻塞
  • doAcquireShared方法
private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; 
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
2.3.3.1.2.1. 加入AQS队列并阻塞
  • addWaiter方法
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

参考:5.AQS.md

2.3.4. 读锁解锁流程

  • ReentrantReadWriteLock.ReadLock.unlock方法
public void unlock() {
    // 调用AQS释放共享锁
    sync.releaseShared(1);
}
2.3.4.1. 借助AQS释放共享锁
  • AbstractQueuedSynchronizer.releaseShared方法
public final boolean releaseShared(int arg) {
    // 调用Sync尝试释放共享锁,如果释放成功则执行doReleaseShared
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
2.3.4.1.1. 利用Sync尝试释放锁
  • ReentrantReadWriteLock.Sync.tryReleaseShared方法

```java
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/12891.html

(0)
LomuLomu
上一篇 15小时前
下一篇 13小时前

相关推荐

  • 2024 DataGrip最新激活码,DataGrip永久免费激活码2025-01-13 更新

    DataGrip 2024最新激活码 以下是最新的DataGrip激活码,更新时间:2025-01-13 🔑 激活码使用说明 1️⃣ 复制下方激活码 2️⃣ 打开 DataGrip 软件 3️⃣ 在菜单栏中选择 Help -> Register 4️⃣ 选择 Activation Code 5️⃣ 粘贴激活码,点击 Activate ⚠️ 必看!必看! 🔥 …

    2025 年 1 月 13 日
    34500
  • 最新IDEA与PyCharm激活方法 – 永久使用激活码教程

    本教程适用于PyCharm 2024、IDEA、DataGrip、Goland等Jetbrains产品,支持全家桶激活!无论您使用的是Windows、Mac还是Linux,均可按照本教程成功激活PyCharm 2024至2098年。 激活截图展示 首先,我们来展示一下最新版本的PyCharm 2024破解成功的截图,如下所示,您可以看到已经成功激活至2098…

    IDEA破解教程 2025 年 4 月 22 日
    18600
  • 【前端】javaScript

    目录 一、JavaScript概述 1.1 引入方式 二、基础语法 2.1 变量 2.2 数据类型 2.3 运算符 2.4 对象 2.4.1 数组 2.4.2 函数 2.4.3 对象 三、jQuery 3.1 引入依赖 3.2 jQuery语法 3.3 jQuery选择器 3.4 jQuery事件 3.5 操作元素 3.6 常用方法 一、JavaScript…

    2024 年 12 月 28 日
    33100
  • 惊爆!Java跌出前三甲

    重磅!Java退出前三甲 大家好呀,我是R哥。 最近留意到 TIOBE 发布的 2025 年 6 月编程语言排名情况: 排名 语言 占比 涨跌 1 Python 25.87% +10.48% 2 C++ 10.68% +0.65% 3 C 9.47% +0.24% 4 Java 8.84% +0.44% 5 C# 4.69% -1.96% 6 JavaScr…

    2025 年 6 月 24 日
    5300
  • 2024最新版本IDEA激活码永久激活教程

    2024最新版本IDEA激活码永久激活教程 本文适用于 IDEA、PyCharm、DataGrip、Goland 等 JetBrains 产品,涵盖了 JetBrains 全家桶的激活方法! 激活成功的效果截图 为了让大家更直观地了解激活效果,首先分享一下成功激活 IDEA 到 2099 年的截图,大家可以看到这款软件已经完全激活,使用时无需再担心任何限制!…

    2025 年 4 月 22 日
    39300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信