关于ReentrantReadWriteLock的一些问题

关于ReentrantReadWriteLock的一些问题

场景

  • 在使用此Lock时应特别注意: 读 ——> 写 ——> 读 交替使用的场景
  • 在工作使用时,该场景是经常出现的,在非公平锁的
  • 若干个读写线程抢占读写锁
  • 读线程手脚快,优先抢占到读锁(其中少数线程任务较重,执行时间较长)
  • 写线程随即尝试获取写锁,未成功,进入双列表进行等待
  • 随后读线程也进来了,要去拿读锁

关键 JUC 的实现代码

ReentrantReadWriteLock.ReadLock.clas [行:727]

public void lock() { 
sync.acquireShared(1); 
} 

ReentrantReadWriteLock.Sync.class [行:448], 所有线程要获取锁时,需要通过调用readerShouldBlock的方法判断当前线程是否需要被阻塞。

protected final int tryAcquireShared(int unused) { 
/* 
* Walkthrough: 
* 1. If write lock held by another thread, fail. 
* 2. Otherwise, this thread is eligible for 
* lock wrt state, so ask if it should block 
* because of queue policy. If not, try 
* to grant by CASing state and updating count. 
* Note that step does not check for reentrant 
* acquires, which is postponed to full version 
* to avoid having to check hold count in 
* the more typical non-reentrant case. 
* 3. If step 2 fails either because thread 
* apparently not eligible or CAS fails or count 
* saturated, chain to version with full retry loop. 
*/ 
Thread current = Thread.currentThread(); 
int c = getState(); 
if (exclusiveCount(c) != 0 && 
getExclusiveOwnerThread() != current) 
return -1; 
int r = sharedCount(c); 
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); 
} 

ReentrantReadWriteLock.NonfairSync.class [行:671], 此处可见,写锁在尝试获取锁时,是不会阻塞的,而读锁取决于,线程等待队列的队头的下一个等待线程是不是获取写锁,如果是,则将阻塞当前读锁的获取,这是非公平锁的一个特性的重要体现,此处也证明了,写锁优先权。

final boolean writerShouldBlock() { 
return false; // writers can always barge 
} 
final boolean readerShouldBlock() { 
/* As a heuristic to avoid indefinite writer starvation, 
* block if the thread that momentarily appears to be head 
* of queue, if one exists, is a waiting writer. This is 
* only a probabilistic effect since a new reader will not 
* block if there is a waiting writer behind other enabled 
* readers that have not yet drained from the queue. 
*/ 
return apparentlyFirstQueuedIsExclusive(); 
} 

final boolean apparentlyFirstQueuedIsExclusive() { 
Node h, s; 
return (h = head) != null && 
(s = h.next) != null && 
!s.isShared() && 
s.thread != null; 
} 

总结

  • 1.使用ReentrantReadWriteLock时,读锁使用tryLock(xx,xx)的方式获取锁,并处理超时的逻辑
  • 2.写锁由于具有优先权,可以使用lock, 但是应使用
writeLock.lock(); 
readLock.lock(); 
writeLock.unlock(); 
readLock.unlock(); 

之所以上面的可以,是利用写锁具有优先权,同时,当当前线程已有写锁,自然就可以使用读锁, 这样就可以防止多个读锁与写锁在多个不同线程中交替出现,以此防止死锁问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值