AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁

AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁

AQS简介

AQS(AbstractQueuedSynchronizer)是一个基于FIFO队列实现同步器,帮我们实现了入队规则线程队列的唤醒工作。
Lock锁就是基于AQS实现的,可实现独占锁,共享锁和信号量。

AQS维护了两种队列,一个是本身的FIFO阻塞队列(结点叫做Node),另一个基于ConditionObject的条件队列

AQS主要包含两个重要的内部类Node和ConditionObject在这里插入图片描述

Node

Node是对线程的封装,它有个核心状态waitStatus,决定当前结点的后续操作(是否等待唤醒还唤醒后续结点),有如下四种状态

    // 表示线程已取消调度
    static final int CANCELLED =  1;
    // 等待唤醒
    static final int SIGNAL    = -1;
    // 等待同步锁
    static final int CONDITION = -2;
    // 共享模式下无条件传播(递归唤醒下一个结点线程)
    static final int PROPAGATE = -3;
  • CANCEL(1):当timeout或中断可变更为此状态,进入该状态的结点将不再发送变化
  • SIGNAL(-1):表示后续结点依赖当前结点唤醒,后继结点入队时,会将前继结点置为SIGNAL
  • CONDITION(-2):表示在获取对应条件的同步锁,当有线程调用Condition.signal()后,处于条件队列的线程就会转移到阻塞队列竞争锁
  • PROPAGATE(-3):共享模式下,前继结点会不断递归唤醒下一个结点
  • 0:新节点的默认状态

负值表示为有效状态

ConditionObject

比如ReentrantLock.newCondition()就是基于ConditionObject实现的,条件队列的结点也是Node

    public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

AQS阻塞队列和条件队列

  1. 当多个线程调用lock.lock()的时候只有一个获取到了锁,其他的就会转为node结点添加到阻塞队列中,并CAS自旋竞争锁。
  2. 当获取锁的线程的条件变量的await()被调用时,就会释放当前锁,并加入到条件变量对应的条件队列中
  3. 有一个线程调用了条件变量的signal()或signalAll()方法,就会把对应条件队列中的线程加入到阻塞队列中

基于AQS实现并发锁

可重写以下四个方法操控资源变量state

  • tryAcquire(int arg):获取独占锁

  • tryRelease(int arg):释放独占锁

  • tryAcquireShared(int arg):获取共享锁

  • tryReleaseShared(int arg):释放共享锁

ReentrantLock 实现原理

Lock锁可看作对synchronized的api扩充,可设置获取锁的超时时间,可通过tryLock得知加锁是否成功,可中断,可公平/不公平,并可以细分读写锁提升效率。

基于AQS设置公平(默认)和非公平锁
有个带参的构造方法,根据参数构建对应的同步器sync

	...
    private final Sync sync;
    ...
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

这个FairSync和NonfairSync都继承了AbstractQueuedSynchronizer(AQS)
AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁_第1张图片

  • 公平锁FairSync
    FairSync的tryAcquire()方法如下,新线程来获取同步锁的时候,通过hasQueuedPredecessors()判断是否有等待队列
    AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁_第2张图片
    AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁_第3张图片

  • 非公平锁NonfairSync
    NonfairSync直接进行CAS加入到等待队列中
    AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁_第4张图片

AQS Node ConditionObject阻塞队列 条件队列 ReentrantLock 公平锁/非公平锁_第5张图片

总结,基于AQS,Lock锁不用再去考虑线程入队和线程的唤醒的逻辑,只需重写tryAcquire/tryRelease或tryAcquireShared/tryReleaseShared方法去操纵信号量state即可。另外,要理解好AQS阻塞队列和条件队列的含义和逻辑,核心就是Node的waitStatus表示了线程的状态,ConditionObject是条件队列的核心。

你可能感兴趣的:(java基础,java)