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;
        }
相关推荐
紧跟先前的步伐4 分钟前
java常用类(下)
java·开发语言·算法
uhakadotcom27 分钟前
2025年java技术发展趋势展望
java·后端·架构
陶然同学29 分钟前
解密MQTT协议:从QOS到消息传递的全方位解析
java·物联网·mqtt·mq·emqx
m0_7482407640 分钟前
go语言的成神之路-筑基篇-gin常用功能
java·golang·gin
橘子海全栈攻城狮1 小时前
【源码+文档+调试讲解】电影交流平台小程序
java·开发语言·servlet·微信小程序·小程序
zjw_rp1 小时前
springmvc-拦截器-异常处理
java·spirngmvc
龙少95431 小时前
【springboot中最适合用什么技术来实现在线聊天】
java·spring boot·后端
陶然同学1 小时前
【小程序】wxss与rpx单位以及全局样式和局部样式
java·微信小程序·小程序
@Java小牛马1 小时前
排序算法原理及其实现
java·数据结构·算法·排序算法
vvw&1 小时前
如何在 Ubuntu 22.04 上安装并开始使用 RabbitMQ
java·linux·运维·服务器·spring·ubuntu·rabbitmq