从ReentrantLock入手AQS源码解析
从ReentrantLock入手AQS源码解析
ReentrantLock的原理:
Lock接口的实现类,基本都是通过聚合了一个队列同步器的子类完成线程访问控制的
以非公平锁ReentrantLock()为例作为突破走起---方法lock():
对比公平锁和非公平锁的tryAcquire()方法的实现代码,其实差异就在于非公平锁获取锁时比公平锁中少了一个判断!hasQueuedPredecessors(),hasQueuedPredecessors()中判断了是否需要排队,导致公平锁和非公平锁的差异如下:
- 公平锁:公平锁讲究先来后到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入到等待队列中;
- 非公平锁:不管是否有等待队列,如果可以获取到锁,则立刻占有锁对象。也就是说队列的第一个排队线程苏醒后,不一定就是排头的这个线程获得锁,它还需要参加竞争锁(存在线程竞争的情况下),后来的线程可能不讲武德插队夺锁了
这里非公平锁下会加一步:新线程不排队直接尝试抢锁
- acquire():
①:tryAcquire(arg):尝试获取独占锁
- 先获取同步状态state,若状态为0(锁空闲),则通过 CAS 抢占锁并标记当前线程为独占线程;
- 若当前线程已是锁的持有者(重入场景),则增加state计数(支持可重入);
- 以上均不满足则返回false,表示获取锁失败。
②addwaiter(Node.EXCLUSIVE):将当前线程封装为 Node 节点并加入同步队列
③acquireQueued(addWeiter(Node.EXCLUSIVE), arg)-----坐稳队列
- 线程在队列中循环检查:若前驱是头节点,尝试tryAcquire获取资源;
- 若获取失败,调用shouldParkAfterFailedAcquire修改前驱节点状态为SIGNAL,再通过parkAndCheckInterrupt挂起当前线程,直到被唤醒后继续竞争。
通过parkAndCheckInterrupt()调用LockSupport.park(this),将当前线程挂起(阻塞),直到被unpark()唤醒或被中断
以非公平锁ReentrantLock()为例作为突破走起---方法unlock()
调用 unlock() → 触发 release(1);
tryRelease 减重入计数,计数归 0 则清空锁持有者;
释放成功后,unparkSuccessor 唤醒同步队列中有效后继线程,完成锁释放与线程唤醒。

浙公网安备 33010602011771号