ReentrantReadWriteLock出现的原因
- 首先synchronized和ReentrantLock都是互斥锁,一个线程在获取锁资源之后另一个线程只能等待
- 假设有一种情况是读多写少,并且确保线程安全。可以使用ReentrantReadWriteLock实现
- ReentrantReadWriteLock的特点是读读不互斥,可以并发执行;读写操作则是互斥的。
代码效果显示
/** * @author 舒一笑 * @date 2023/6/1 */ public class Test17 { static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); static ReentrantReadWriteLock.ReadLock readLock =lock.readLock(); public static void main(String[] args) throws InterruptedException { new Thread(() -> { // 这里现在是读锁 readLock.lock(); try { System.out.println("子线程是读锁"); Thread.sleep(50000000); } catch (InterruptedException e) { throw new RuntimeException(e); }finally { readLock.unlock(); } }).start(); Thread.sleep(1000); // 这里现在也是读锁 readLock.lock(); try { System.out.println("主线程"); } finally { readLock.unlock(); } } }
- 读读锁效果演示
- 写读锁效果演示
ReentrantReadWriteLock(重新输入读写锁定)锁的实现原理分析
- 还是基于AQS实现,同样都是对state的操作。获取锁资源成功便执行判断之后的方法体逻辑,否则便会阻塞到AQS队列中去排队
- 查看对AQS方法在ReentrantReadWriteLock中的实现可以知道
- 读锁操作是基于state的高16位的操作
- 写锁操作是基于state的低16位的操作,所以在锁重入的时候同样是对state的操作,但是范围却比小了
- ReentrantReadWriteLock依旧是可重入锁
tryAcquire(int acquires)方法的分析
- 当前线程不持有锁资源,c的值是0;尝试获取锁资源、CAS拿锁。
写锁释放锁流程和源码分析
- tryRelease(arg)方法分析
读锁分析
- 读锁加锁源码分析
- 方法体里面没拿到锁资源就去排队
- tryAcquireShared(arg)方法分析
- 读锁重入流程分析
- 读锁的重入主要就是基于下面这部分的源码的实现。
- 通过对r是否为0的判断来证明当前是不是第一个拿到读锁资源的线程
- 判断满足那么将firstRead置为当前线程。并将firstReadCount置为1
- 要是第一个判断不满足。那么便会判断当前线程是不是第一个获取读锁资源的线程。要是是的话那么就会firstReadHoldcount++
- 要是还是不满足那么就说明当前线程不是第一个获取读锁资源的线程。那就在方法体中获取最后一个拿到锁资源的线程。并判断当前线程是否是最后一个拿到读锁资源的线程。要是不是那就将当前线程设置为cachedHoldCounter。
- 如果在最内部的判断中当前线程是之前的cachedHoldCounter,那就判断当前的重入次数是不是0,重新设置当前线程锁重入信息到readHolds,就是包装了ThreadLocal中,完成初始化操作。重入次数是0;将count次数++
- 读锁加锁后续fullTryAcquireShared(current)方法分析
- 读锁在获取锁资源之后doAcquireShared(arg)方法分析
setHeadAndPropagate(node, r);方法分析
读锁释放锁流程
- doReleaseShared()方法分析