在本课时我们主要讲解读锁应该插队吗?以及什么是读写锁的升降级。
读锁插队策略
首先,我们来看一下读锁的插队策略,在这里先快速回顾一下在 24 课时公平与非公平锁中讲到的 ReentrantLock,如果锁被设置为非公平,那么它是可以在前面线程释放锁的瞬间进行插队的,而不需要进行排队。在读写锁这里,策略也是这样的吗?
首先,我们看到 ReentrantReadWriteLock 可以设置为公平或者非公平,代码如下:
公平锁:
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(true);
非公平锁:
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);
如果是公平锁,我们就在构造函数的参数中传入 true,如果是非公平锁,就在构造函数的参数中传入 false,默认是非公平锁。在获取读锁之前,线程会检查 readerShouldBlock() 方法,同样,在获取写锁之前,线程会检查 writerShouldBlock() 方法,来决定是否需要插队或者是去排队。
首先看公平锁对于这两个方法的实现:
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
很明显,在公平锁的情况下,只要等待队列中有线程在等待,也就是 hasQueuedPredecessors() 返回 true 的时候,那么 writer 和 reader 都会 block,也就是一律不允许插队,都乖乖去排队,这也符合公平锁的思想。
下面让我们来看一下非公平锁的实现:
final boolean writerShouldBlock() {
return false; // writers can always barge
}
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
在 writerShouldBlock() 这个方法中始终返回 false,可以看出,对于想获取写锁的线程而言,由于返回值是 false,所以它是随时可以插队的,这就和我们的 ReentrantLock 的设计思想是一样的,但是读锁却不一样。这里实现的策略很有意思,先让我们来看下面这种场景:
假设线程 2 和线程 4 正在同时读取,线程 3 想要写入,但是由于线程 2 和线程 4 已经持有读锁了,所以线程 3 就进入等待队列进行等待。此时,线程 5 突然跑过来想要插队获取读锁:

面对这种情况有两种应对策略:

本课时探讨Java读写锁中的读锁插队策略,ReentrantReadWriteLock实现选择了不允许插队以避免写锁线程饥饿。同时介绍了锁的降级功能,通过示例解释其在更新缓存场景中的应用,强调了降级以提高并发性能的重要性。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



