概述
维护了一对关联的锁,一个用于只读操作,另一个用于写操作。其中写锁是独占的,只要没有写操作,多个读线程可以同时持有读锁。也就是说,成功获取读锁的线程将看到在先前的写锁上所做的所有更新。
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线程获取写锁
(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线程获取写锁
(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获取读锁
(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
结论:当写线程获得写锁后,后续不管是读线程,还是写线程,一律进入队列阻塞等待写锁被释放。后续被唤醒的线程如果是读线程获得锁后,接着要判断下一个被阻塞的线程是否也是读线程,如果是,则一并唤醒;否则,等待被唤醒的读线程释放读锁。