文章目录
概要
- RentrantReadWriteLock如何做锁降级?
- RentrantReadWriteLock可以做锁降级,为何无法做锁升级?
RentrantReadWriteLock
它是 ReadWriteLock 读写锁的默认实现,也能实现公平/非公平两种锁, 还能获取到读锁和写锁;
读锁和写锁
这两个名词很好理解,就是字面意思:
- 读锁: 做的是读操作,例如: 读取数据库值,读取一个属性值,获取集合中的一个值等…,对这种操作加一把锁;
- 写锁: 做的是写操作,例如: dml数据库,删除集合中的一个元素,更新集合中的元素等…,对这种操作加一把锁;
读写锁互斥原则
只要写锁获取成功,那么其他读写锁线程均阻塞;
当获取写锁的时候,如果该资源已被其他线程获取到读锁,那么写锁也会获取失败;(保证可见性)
- 读锁与读锁之间可以共享;
- 写锁与读锁/写锁均互斥;
技术细节:锁降级
锁降级指的是一个写锁,最终被降级成了读锁
伪代码如下:
public void processData() {
readLock.lock();
if (!update) {
// 必须先释放读锁
readLock.unlock();
// 锁降级从写锁获取到开始
writeLock.lock();
try {
if (!update) {
// 准备数据的流程(略)
update = true;
}
readLock.lock();
} finally {
writeLock.unlock();
}
// 锁降级完成,写锁降级为读锁
}
try {
// 使用数据的流程(略)
} finally {
readLock.unlock();
}
}
这段代码中,锁降级由读锁开始,最终由读锁结束,但是中间,经历了很多;
读锁的最开始和最终结束,不是一个锁;
这里获取写锁也很好理解,因为我要完成对于update 的赋值,既修改,这是写操作;
在获取到写锁后,操作update ,在释放写锁之前,获取到一个读锁,然后再释放写锁;(这里是重点,为何??)
最终释放读锁,完成了锁的降级,从写锁,降级为读锁;
重点解析
在获取到写锁后,操作update ,在释放写锁之前,获取到一个读锁,然后再释放写锁;(这里是重点,为何??)
为何要在写锁中获取一个读锁,这样做的目的是什么?
1 当执行这段代码的时候,只有一个线程能够获取到写锁,其他线程会被阻塞在读锁和写锁的lock()方法上,如果不获取读锁,那么其他线程可能会立马获取到读锁,并修改update,会造成当前线程无法感知到变化,因为判断已经结束;
2 锁降级中读锁的获取是必要的,因为,当我获取到读锁之后,其他线程的写锁是无法获取成功的(保证可见性),其他读锁是可以获取成功的,所以这里读锁的获取,主要是为了防止其他写锁获取成功;最终完成了锁的降级
为何RentrantReadWriteLock不能做锁的升级?
目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。