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;
        }
相关推荐
lhldsg几秒前
智慧场馆解决方案小程序系统:从架构设计到落地实践
java·小程序·需求分析
hfywmsj42 分钟前
广州餐饮铺位招租的选址架构:流量入口与成本函数分析
java·大数据·jvm·广州餐饮铺位招租
爪哇岛国人1 小时前
原来我一直理解错了:实现接口真的必须实现所有方法吗?
java
万年咸鱼1 小时前
Java BufferedOutputStream 详解:原理、用法与性能优化
java·开发语言·性能优化
万年咸鱼1 小时前
Java BufferedInputStream 详解:原理、用法与实战
java·开发语言·python
~木雨1 小时前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
艾莉丝努力练剑2 小时前
【AI大模型接入SDK】Gemini模型接入知识体系
java·开发语言·网络·人工智能·网络协议·学习·http
云运维笔记2 小时前
Zabbix 分布式监控搭建实战:基于 Proxy 实现 MySQL、Java、Nginx 监控
java·mysql·zabbix
zhanghaha13142 小时前
Python进阶教程:28_queue 队列模块 零基础超详细教程
java·开发语言·python
司小豆2 小时前
第七课:DeepSeek Harness 服务与依赖注入
java·服务器·开发语言·github·ai编程