10 读写锁ReentrantReadWriteLock

1 介绍

为什么要使用读写锁?

需要高并发读取和较低并发写入的应用程序,降低锁的粒度,提高系统性能

使用场景

读多写少的共享资源

缓存管理:读 >> 写,控制多个线程同时读缓存,需要刷新or修改操作时才使用写锁

数据库连接池:多个线程从池中获取连接(读操作),只有一个线程可以设置连接到池中(写操作)

文件读写

数据结构的并发访问

2 使用

java 复制代码
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class SharedResource {
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    public void readFromResource() {
        readLock.lock(); // 获取读锁
        try {
            // 执行读取共享资源的操作
        } finally {
            readLock.unlock(); // 释放读锁
        }
    }

    public void writeToResource() {
        writeLock.lock(); // 获取写锁
        try {
            // 执行写入共享资源的操作
        } finally {
            writeLock.unlock(); // 释放写锁
        }
    }
}

3 原理分析

读写锁两个状态,读状态、写状态

但AQS中只有一个state,如何记录两种状态?

高低位;int4个字节,共32位,采用高16位控制读,低16位控制写

00000000 00000000 00000000 00000000

加锁时如何判断读锁、写锁?

高16位>0,表示有读锁(sharedCount())

低16位>0,表示有写锁(exclusiveCount())

如何实现可重入?

写锁只有一个线程独占,重入则低16位+1即可

写锁有多个线程持有,如何记录?ThreadLocal线程私有

4 读锁源码

读锁:tryAcquireShared()、tryReleaseShared();读读共享

java 复制代码
protected final int tryAcquireShared(int unused) {
            Thread current = Thread.currentThread();
            int c = getState();
            //是否其它线程占用排它锁,如果是则不允许获取共享锁
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //共享锁被获取的数量
            int r = sharedCount(c);
            //获取共享锁
            //1 未阻塞
            //2 不超最大读计数
            //3 设置共享锁成功
            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);
        }
java 复制代码
final int fullTryAcquireShared(Thread current) {
            HoldCounter rh = null;
            //1 自旋 获取共享锁or失败
            for (;;) {
                int c = getState();
                //2 若存在写锁,且不是当前线程持有的,不允许获取共享锁
                if (exclusiveCount(c) != 0) {
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                //3 读线程阻塞
                } 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;
                    }
                }
                // 4 不允许再申请共享锁
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // 5 尝试获取锁
                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; // cache for release
                    }
                    return 1;
                }
            }
        }

5 写锁源码

写锁:tryAcquire()、tryRelease();写写互斥,读写互斥

java 复制代码
protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
相关推荐
num_killer7 小时前
小白的Langchain学习
java·python·学习·langchain
期待のcode8 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐8 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲8 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红8 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥8 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v9 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地9 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209259 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei9 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot