java readandwrite_Java并发之ReentrantReadWriteLock设计实现详解

本文详细解析了Java并发中ReentrantReadWriteLock的设计与实现,包括其适用场景、内部变量的意义以及读写交替场景的分析,揭示了读锁的共享性质以及在有读写操作时线程的阻塞与唤醒机制。
摘要由CSDN通过智能技术生成

概述

3b84b155148ee32362193a627d0b3a79.png

维护了一对关联的锁,一个用于只读操作,另一个用于写操作。其中写锁是独占的,只要没有写操作,多个读线程可以同时持有读锁。也就是说,成功获取读锁的线程将看到在先前的写锁上所做的所有更新。

1、不频繁写,频繁读,适合使用读写锁。否则,大部分时间会被写锁独占。

2、如果读操作过于短,则读写锁的实现会占用绝大部分性能开销,因为比一般的互斥锁执行逻辑更复杂。

变量含义

理解下面的变量含义,有助于理解代码。

State:用一个整型数来维护读写锁数量,高16位为读锁、低16位为写锁。

QueueLength:当线程获取写锁后,其他待获取读、写锁的线程阻塞在队列中。或读锁线程未释放,写锁线程阻塞在队列中。

ReadHoldCount:当前线程持有读锁的数量。即调用一次lock自增1,用于实现可重入。

ReadLockCount:共有多少个不同的线程获取到读锁。

WriteHoldCount:当前线程持有写锁的数量。即调用一次lock自增1,用于实现可重入。请思考为什么没有WriteLockCount?

FirstReader、FirstReaderHoldCount:表示第一个获取读锁的线程和重入次数,避免每次都从readHolds中查找,用于提高性能。

CacheHoldCounter:表示最后一个获取读锁的线程和重入次数,避免每次都从readHolds中查找,用于提高性能。

ReadHolds:保存除第一和最后一个获取读锁的线程外的其他线程和重入次数。

ExclusiveOwnerThread:表示获取写锁的线程。

Fair和Nonfair:

对于Fair,不管是读线程,还是写线程获取锁前,都要判断队列中是否有等待未被唤醒的线程。如果有,则进入等待队列阻塞。

对于Nonfair,如果是写线程不管队列中是否有阻塞线程,一律优先获取写锁;如果是读线程,判断队列中的第一个等待线程是否是写线程,是则进入队列阻塞。否则,该线程获取读锁。

读写交替场景分析

仔细观察变量状态值变化,辅助源码debug效果更佳。

r1,r2线程获取读锁,全部释放读锁后;w1线程获取写锁

cc8e831711f398ec63963761ba151c2a.png

(1) r1 lock

state:1 00000000 00000000

firstReader:r1

firstReaderHoldCount:1

(2) r2 lock

state:10 00000000 00000000

cachedHoldCounter:r2

cachedHoldCounter.count:1

(3) r2 unlock

state:1 00000000 00000000

cachedHoldCounter:r2

cachedHoldCounter.count:0

(4) r1 unlock

state:00000000 00000000

firstReader:null

(5) w1 lock

state:00000000 00000001

exclusiveOwnerThread:w1

(6) w1 unlock

state:00000000 00000000

exclusiveOwnerThread:null

结论:读锁为共享锁,多个读线程可以共同持有读锁。

r1,r2线程获取读锁,读锁未释放,w1线程获取写锁

0234a6c28a476165cdec779ba8ec2eb9.png

(1) r1 lock

state:1 00000000 00000000

firstReader:r1

firstReaderHoldCount:1

(2) r2 lock

state:10 00000000 00000000

cachedHoldCounter:r2

cachedHoldCounter.count:1

(3) w1 lock(阻塞)

有读锁未释放,w1写线程进入等待队列。在读锁未释放完前,w1写线程阻塞。

(4) r2 unlock

state:1 00000000 00000000

cachedHoldCounter:r2

cachedHoldCounter.count:0

(5) r1 unlock

state:00000000 00000000

firstReader:null

当前全部读锁释放,通知w1写线程

(6) w1 lock(唤醒)

state:00000000 00000001

exclusiveOwnerThread:w1

(7) w1 unlock

state:00000000 00000000

exclusiveOwnerThread:null

结论:当读锁未完全释放时,独占写锁要进入等待队列阻塞,独占写锁等待全部读锁释放完成后被唤醒继续获取读锁。

w1线程获取写锁,未释放,r1获取读锁,w2获取写锁,r2获取读锁

8f3c0dc642d40fc0b4305cfc01f155e6.png

(1) w1 lock

state:00000000 00000001

exclusiveOwnerThread:w1

(2) r1 lock(阻塞)

state:00000000 00000001

exclusiveOwnerThread:w1

有写锁未释放,r1读线程进入等待队列。在写锁未释放前,r1读线程阻塞。

(3) w2 lock(阻塞)

state:00000000 00000001

exclusiveOwnerThread:w1

有写锁未释放,w2写线程进入等待队列。

(4) r2 lock(阻塞)

state:00000000 00000001

exclusiveOwnerThread:w1

有写锁未释放,r2读线程进入等待队列。

(5) w1 unlock

state:00000000 00000000

exclusiveOwnerThread:null

唤醒队列中第一个被阻塞的线程r1

(6) r1 lock(唤醒)

state:1 00000000 00000000

firstReader:r1

firstReaderHoldCount:1

r1出队列获得读锁,接着判断下一个队列阻塞线程是否也为待获取读锁的线程。如果是,则唤醒后续读线程;否则,后续是阻塞写线程,仍阻塞,等待r1释放锁。

(7) r1 unlock

state:00000000 00000000

firstReader:null

唤醒队列中第一个被阻塞的线程w2

(8) w2 lock(唤醒)

state:00000000 00000001

exclusiveOwnerThread:w2

w2出队

(9) w2 unlock

state:00000000 00000000

exclusiveOwnerThread:null

唤醒队列中第一个被阻塞的线程r2

(10) r2 lock(唤醒)

state:1 00000000 00000000

firstReader:r2

firstReaderHoldCount:1

(11) r2 unlock

state:00000000 00000000

firstReader:null

结论:当写线程获得写锁后,后续不管是读线程,还是写线程,一律进入队列阻塞等待写锁被释放。后续被唤醒的线程如果是读线程获得锁后,接着要判断下一个被阻塞的线程是否也是读线程,如果是,则一并唤醒;否则,等待被唤醒的读线程释放读锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值