前言
上篇文章写了synchronized的锁升级,本文主要来写下java中的锁降级,其实在很多书中和博客里的观点都是没有锁降级这一说的,但是java中确实有锁降级的场景。
锁降级
锁降级发生在读写锁中,写锁降级读锁的过程。
读写锁
ReentrantReadWriteLock
读写锁,既可以获取读锁,也可以获取写锁
需要明确的是:
- 写锁是独占锁,所谓独占即为独自占有,别的线程既不能获取到该锁的写锁,也不能获取到对应的读锁。
- 读锁是共享锁,所谓共享即是所有线程都可以共同持有该读锁。
两个重点:
- 当一个线程获取了写锁(未释放),其他线程既不可以获取到写锁,也不能获取到读锁。
- 当一个线程获取到了读锁(未释放),其他线程可以获取到读锁,但是不能获取到写锁。
代码验证
/**
*
* @author wengyz
* @version ReadAndWriteLockDemo.java, v 0.1 2020-05-05 15:15
*/
public class ReadAndWriteLockDemo {
public static void main(String[] args) {
// 读写锁
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 读锁
ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
// 写锁
ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
/**
* 写锁一
*/
new Thread(new Runnable() {
@Override
public void run() {
writeLock.lock();
System.out.println("写锁一加锁成功");
writeLock.unlock();
}
}).start();
/**
* 读锁一
*/
new Thread(new Runnable() {
@Override
public void run() {
readLock.lock();
System.out.println("读锁一加锁成功");
readLock.unlock();
}
}).start();
/**
* 读锁二
*/
new Thread(new Runnable() {
@Override
public void run() {
readLock.lock();
System.out.println("读锁二加锁成功");
readLock.unlock();
}
}).start();
/**
* 写锁二
*/
new Thread(new Runnable() {
@Override
public void run() {
writeLock.lock();
System.out.println("写锁二加锁成功");
writeLock.unlock();
}
}).start();
}
}
结论:如果写锁一不释放,下面的读锁一、二和写锁二都将获取不到锁,如果读锁一不释放,读锁二可以获取到锁,但是写锁二将会获取不到锁。
这样设计的目的,是为了保证读取的数据的不存在脏数据。
锁降级分析
锁降级指的是写锁降级为读锁的过程,他的过程是持有写锁,获取读锁,然后释放写锁。
注意以下情况不是锁降级
这种情况可以理解成是锁竞争,而且此种做法是错误的,不能保证再次获取读锁的时候没有写锁曾经占有过,即不能保证数据的可见性。
锁降级的本质是释放掉独占锁,使其他线程可以获取到读锁,提高并发,而当前线程持有读锁来保证数据的可见性。
最后
这个问题是上次面试一个小伙子时,他没有回答好的问题,其实他能知道读写锁的降级已经很不错了,说明私下是有看点东西的,但是确实没有深入的研究过,今天是五一假期的最后一天索性写篇文章分享下。