【JDK源码分析系列】AbstractQueuedSynchronizer 源码分析 -- 基本属性

【JDK源码分析系列】AbstractQueuedSynchronizer 源码分析 -- 基本属性

【1】AbstractQueuedSynchronizer 整体架构

1. AQS 中队列只有两个 : 同步队列 + 条件队列,底层数据结构两者都是链表
2. 图中有四种颜色的线代表四种不同的场景,1、2、3 序号代表看的顺序

【2】AbstractQueuedSynchronizer 基本属性

【2.0】AbstractQueuedSynchronizer 的定义与说明

// 1. 提供了一种框架,自定义了先进先出的同步队列,让获取不到锁的线程能进入同步队列中排队;
// 2. 同步器有个状态字段,可以通过状态字段来判断能否得到锁,此时设计的关键在于依赖安全的 atomic value 来表示状态
//   (虽然注释是这个意思,但实际上是通过把状态声明为 volatile,在锁里面修改状态值来保证线程安全的);
// 3. 子类可以通过给状态 CAS 赋值来决定能否拿到锁,可以定义那些状态可以获得锁,哪些状态表示获取不到锁
//   (比如定义状态值是 0 可以获得锁,状态值是 1 就获取不到锁);
// 4. 子类可以新建非 public 的内部类,用内部类来继承 AQS,从而实现锁的功能;
// 5. AQS 提供了排它模式和共享模式两种锁模式,排它模式下:只有一个线程可以获得锁,
//    共享模式可以让多个线程获得锁,子类 ReadWriteLock 实现了两种模式;
// 6. 内部类 ConditionObject 可以被用作 Condition,通过 new ConditionObject () 即可得到条件队列;
// 7. AQS 实现了锁、排队、锁队列等框架,至于如何获得锁、释放锁的代码并没有实现,
//      比如 tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared、isHeldExclusively 这些方法,
//      AQS 中默认抛 UnsupportedOperationException 异常,都是需要子类去实现的;
// 8. AQS 继承 AbstractOwnableSynchronizer 是为了方便跟踪获得锁的线程,可以帮助监控和诊断工具识别是哪些线程持有了锁;
// 9. AQS 同步队列和条件队列,获取不到锁的节点在入队时是先进先出,但被唤醒时,可能并不会按照先进先出的顺序执行;

// AQS 是个抽象类,就是给各种锁子类继承用的,AQS 定义了很多如何获得锁,如何释放锁的抽象方法,目的就是为了让子类去实现;
// 继承了 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 的作用
// 就是为了知道当前是那个线程获得了锁,方便监控用的;
// AbstractOwnableSynchronizer 类中 private transient Thread exclusiveOwnerThread; 属性用于描述当前获得锁的线程
// setExclusiveOwnerThread 方法用于设置当前获取锁的线程
// protected final void setExclusiveOwnerThread(Thread thread) {
//     exclusiveOwnerThread = thread;
// }

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

【2.1】AbstractQueuedSynchronizer 简单属性

// 同步器的状态,根据当前状态进行判断是否可以获得当前锁
// 如果当前state是0,那么可以获得锁
// 可重入锁,每次获得锁+1,每次释放锁-1
private volatile int state;

// 自旋超时阀值,单位纳秒
// 当设置等待时间时才会用到这个属性
static final long spinForTimeoutThreshold = 1000L;

【2.2】AbstractQueuedSynchronizer 同步队列属性

同步队列:底层数据结构是一个双向链表,当多个线程都来请求锁时,某一时刻有且只有一个线程能够获得锁 (排它锁),那么剩余获取不到锁的线程,都会到同步队列中去排队并阻塞自己,当有线程主动释放锁时,就会从同步队列头开始释放一个排队的线程,让线程重新去竞争锁,所以同步队列的主要作用阻塞获取不到锁的线程,并在适当时机释放这些线程。

// 同步队列的头。
// 公平的锁先入先出。
private transient volatile Node head;

// 等待队列的尾
private transient volatile Node tail;

【2.3】AbstractQueuedSynchronizer 条件队列属性

条件队列:条件队列管理获取不到锁的线程,底层数据结构也是链表队列,但条件队列不直接和锁打交道,但常常和锁配合使用,是一定的场景下,对锁功能的一种补充。

private static final long serialVersionUID = 1173984872572414699L;
// 条件队列中第一个 node
private transient Node firstWaiter;
// 条件队列中最后一个 node
private transient Node lastWaiter;

【2.4】AbstractQueuedSynchronizer Node 属性

//Node 既是同步队列的节点,又是条件队列的节点,
//在入队的时候,用 Node 把线程包装一下,然后把 Node 放入两个队列中
static final class Node {

    //node 是共享模式
    /** 标志用于指示一个节点在共享模式下等待 */
    static final Node SHARED = new Node();

    //node 是排它模式
    /** 标志用于指示一个节点在独占模式下等待 */
    static final Node EXCLUSIVE = null;


    /** 
    * 等待状态值,表示线程被取消
    *
    * 由于在同步队列中等待的线程等待超时或中断,需要从同步队列中取消等待
    * 节点进入该状态后不会发生变化
    */
    // 被取消
    static final int CANCELLED =  1;

    /**
     * 后继结点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,
     * 将会通知后继节点,使后继节点的线程得以运行
     */
    // SIGNAL 状态的意义:同步队列中的节点在自旋获取锁的时候,
    // 如果前一个节点的状态是 SIGNAL,那么自己就可以阻塞休息了,否则自己一直自旋尝试获得锁
    static final int SIGNAL    = -1;

    /**
     * 节点在等待队列中,节点线程等待在Condition上,
     * 当其他线程对Condition调用了Signal()方法后,
     * 该节点将会从等待队列转移到同步队列中,加入到对同步状态的获取中
     */
    // 表示当前 node 正在条件队列中,
    // 当有节点从同步队列转移到条件队列时,状态就会被更改成 CONDITION
    static final int CONDITION = -2;
    /**
     * 表示下一次共享式同步状态获取将会无条件地被传播下去
     */
    // 无条件传播,共享模式下,该状态的进程处于可运行状态
    static final int PROPAGATE = -3;

    /**
     * 等待状态变量
     *
     * volatile 作用简介 : java 线程内存模型将确保所有线程看到的该变量是一致的;
     */
    // 表示当前节点的状态,通过节点的状态来控制节点的行为
    // 普通同步节点,就是 0 ,条件节点是 CONDITION -2
    volatile int waitStatus;

    /**
    * 指向同步队列中当前节点的前驱节点
    */
    // 当前节点的前节点
    // 节点 acquire 成功后就会变成 head
    // head 节点不能被 cancelled
    volatile Node prev;

    /**
    * 指向同步队列中当前节点的后继节点
    */
    // 当前节点的下一个节点
    volatile Node next;

    // 当前节点的线程
    volatile Thread thread;

    // 在同步队列中,nextWaiter 并不真的是指向其下一个节点,我们用 next 表示同步队列的下一个节点,
    // nextWaiter 只是表示当前 Node 是排它模式还是共享模式
    // 但在条件队列中,nextWaiter 就是表示下一个节点元素
    Node nextWaiter;

    /**
     * 该方法用于判断节点是否处于共享状态
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * 该方法用于获取当前节点的前驱节点
     * 在该方法中添加了空指针判断,在前驱节点为空时,抛出空指针异常;
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    /**
     * 内部类 Node 的默认构造方法
     * 
     * 用于创立初始化头结点或者共享节点
     */
    Node() {
    }
    /**
     * 内部类 Node 的构造方法
     *
     * 构造的节点指定等待队列中的节点
     */
    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }
    /**
     * 内部类 Node 的构造方法
     *
     * 构造的节点指定等待的状态
     */        
    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

【2.5】AbstractQueuedSynchronizer 一般方法

【2.5.0】AbstractQueuedSynchronizer 队列操作图示

【2.5.1】AbstractQueuedSynchronizer 相关 CAS 方法

/**
 * CAS 方式设置同步状态;
 */
protected final boolean compareAndSetState(int expect, int update) {
    
    /**
     * Unsafe 类提供了硬件级别的原子操作,提供了一些绕开JVM的更底层功能,由此提高效率;
     * compareAndSwapInt : 比较并交换 int 型变量
     *
     * 参数说明
     * 参数包含 : 需要读写的内存位置(stateOffset)、进行比较的预期原值(expect)
     * 和拟写入的新值(update);
     * 处理方式 : 如果内存位置stateOffset的值与预期原值expect相匹配,
     * 那么处理器会自动将该位置值更新为新值update;
     * 否则处理器不做任何操作。
     */
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

/**
 * CAS head field. Used only by enq.
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
 * CAS tail field. Used only by enq.
 */
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

/**
 * CAS waitStatus field of a node.
 */
private static final boolean compareAndSetWaitStatus(Node node,
                                                        int expect,
                                                        int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                    expect, update);
}

/**
 * CAS next field of a node.
 */
private static final boolean compareAndSetNext(Node node,
                                                Node expect,
                                                Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

【2.5.2】AbstractQueuedSynchronizer 相关线程处理方法

private final boolean parkAndCheckInterrupt() {
    //阻塞当前线程
    LockSupport.park(this);
    //中断当前线程
    return Thread.interrupted();
}

【3】条件 (Condition)

【3.0】Condition 的定义与说明

// 1. 当 lock 代替 synchronized 来加锁时,Condition 就可以用来代替 Object 中相应的监控方法了,
//  比如 Object#wait ()、Object#notify、Object#notifyAll 这些方法
// 2. 提供了一种线程协作方式:一个线程被暂停执行,直到被其它线程唤醒
// 3. Condition 实例是绑定在锁上的,通过 Lock#newCondition 方法可以产生该实例
// 4. 除了特殊说明外,任意空值作为方法的入参,都会抛出空指针
// 5. Condition 提供了明确的语义和行为,这点和 Object 监控方法不同
public interface Condition {

【3.1】Object 的监视器方法与 Condition 接口对比

【3.2】Condition 常用方法以及描述

 参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】Java并发编程的艺术

【2】面试官系统精讲Java源码及大厂真题/30 AbstractQueuedSynchronizer 源码解析(上)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章