10 读写锁ReentrantReadWriteLock

编程入门 行业动态 更新时间:2024-10-26 22:26:26

10 读写锁ReentrantReadWriteLock

10 读写锁ReentrantReadWriteLock

1 介绍

为什么要使用读写锁?

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

使用场景

        读多写少的共享资源

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

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

        文件读写

        数据结构的并发访问

2 使用

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();读读共享

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);}
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();写写互斥,读写互斥

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 acquiresetState(c + acquires);return true;}if (writerShouldBlock() ||!compareAndSetState(c, c + acquires))return false;setExclusiveOwnerThread(current);return true;}

更多推荐

10 读写锁ReentrantReadWriteLock

本文发布于:2023-12-04 18:19:52,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1661813.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:ReentrantReadWriteLock

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!