二十、ReentrantReadWriteLock之读写锁基础

47 阅读2分钟

ReentrantReadWriteLock之读写锁基础

  • ReentrantReadWriteLock的读锁之间是不互斥的
  • ReentrantReadWriteLock的写锁之间,写锁与读锁之间是互斥的
  • ReentrantReadWriteLock适用于读写并发,并且读多写少的场景

ReentrantReadWriteLock的初始化:

private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 读锁初始化
private static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
// 写锁初始化
private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

读写锁的实现原理

ReentrantReadWriteLock是基于AQS实现的,还是对state进行操作,拿到锁资源就去干活,没有拿到,放到AQS中排队

  • ReentrantReadWriteLock是可重入锁
  • 读锁操作:基于state的高16位进行操作
  • 写锁操作:基于state的低16位进行操作

写锁重入:

读写锁中的写锁重入方式基本和ReentrantLock一致,依然是对state进行+1操作。只要确认持有锁资源的线程是当前线程即可。只不过之前ReentrantLock的重入次数比写锁多

读锁重入:

因为读锁是共享锁,所以同一时间会有多个读锁线程持有锁资源。这样一来,无法确认每个线程读锁重入的次数。为了记录重入次数,每个读锁线程都会用ThreadLocal去记录。

写锁的饥饿问题:

读锁是共享锁,当有线程持有锁资源时,再来一个线程想要获得写锁,此时,大量想要获得读锁的线程来申请锁资源,如果可以绕过写锁直接拿到资源,会造成写锁长时间无法获得锁资源的情况

解决方式:读锁在拿到锁资源后,如果再有线程想要获得读锁资源,先判断同步队列中是否有写锁线程,如果有,需要去同步队列中排队。持有读锁的线程只会让写锁线程之前的读线程拿到锁资源