AQS源码解析(一)

父类AbstractOwnableSynchronizer源码解析

AbstractOwnableSynchronizer为创建可能需要所有权概念的锁和同步器提供了基础

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;
    /构造器
    protected AbstractOwnableSynchronizer() { }
    /独占模式同步器的当前所有者(一个线程对象)
    private transient Thread exclusiveOwnerThread;
    /设置独占模式同步器的所有者
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }
    /返回独占模式同步器的当前所有者
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

AQS内部类Node源码解析

/**
*等待队列的节点类
*通过Node可以实现两个对列
*1.通过prev和next实现CLH对列(线程同步对列,双向对列)
*2.nextWaiter实现Condition条件上的等待线程对列(单向对列)
*/
static final class Node {
    //标识节点当前在共享模式下
    static final Node SHARED = new Node();
    //标识节点当前在独占模式下
    static final Node EXCLUSIVE = null;
    /**下面几个int常量是给waitStatus用*/
    //此线程已经取消
    static final int CANCELLED =  1;
    //其表示当前node的后继节点对应的线程需要被唤醒
    static final int SIGNAL    = -1;
    //线程在等待condition条件
    static final int CONDITION = -2;
    //共享模式下node有可能处于这种状态,表示锁的下一次获取可以无条件传播
    static final int PROPAGATE = -3;

    /**
     * 取值范围只可能是
     *   SIGNAL:     其表示当前node的后继节点对应的线程需要被唤醒
     *   CANCELLED:  此线程已经取消。可能是超时或者中断。
     *   CONDITION:  此node当前处于条件队列中
     *   PROPAGATE:  当前场景下后续的acquireShared能够得以执行
     *   0:          对于正常的同步节点,该字段初始化为0.
     */
    volatile int waitStatus;

    /**
     * 当前节点的前驱节点,用于检查waitStatus
     * 如当前节点取消,那就需要前驱节点和后继节点来完成连接
     */
    volatile Node prev;

    /**
     * 指向当前节点在释放时唤醒的后续节点
     */
    volatile Node next;

    /**
     * 入队列时的当前节点
     */
    volatile Thread thread;

    /**
     * 存储condition对列中的后继节点
     */
    Node nextWaiter;

    /**
     * 如果节点在共享模式下等待,则返回true
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * 返回当前节点的前驱节点
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }
    /**
    *无参构造器
    */
    Node() { 
    }
    //构造器,用于addWaiter()方法
    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }
    //构造器,用于condition
    Node(Thread thread, int waitStatus) { 
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

AQS实例属性源码解析

/*****AbstractQueuedSynchronizer的实例属性*****/
/**
 * 等待队列的头结点
 */
private transient volatile Node head;
/**
 * 等待队列的尾结点
 */
private transient volatile Node tail;
/**
 * 同步状态
 */
private volatile int state;
/**
 * state的getter方法
 */
protected final int getState() {
    return state;
}
/**
 * state的setter方法
 */
protected final void setState(int newState) {
    state = newState;
}

AQS独占锁加锁

独占锁至少有两个功能:

  1. 获取锁的功能。当多个线程一起获取锁的时候,只有一个线程能获取到锁,其他线程必须在当前位置阻塞等待。
  2. 释放锁的功能。获取锁的线程释放锁资源,而且还必须能唤醒正在等待锁资源的一个线程。

独占锁加锁

acquire()方法

/**
*以独占的方式获取锁资源,不响应中断
*/
public final void acquire(int arg) {
    //1.调用tryAcquire()方法,返回true则结束,
    //tryAcquire()方法一般在AQS子类中实现
    //2.如果tryAcquire()方法返回false
    //调用addWaiter()方法进入队列
    //3.调用acquireQueued()方法等待队列中获取资源
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

方法流程如下:

  1. tryAcquire()方法:尝试以独占方式获取资源,在AQS中是空方法,子类要重写该方法。通常state代表资源。不用锁中释义不同。获取到资源后返回true;反之,返回false,转入下个流程。
  2. addWaiter()方法:将线程加入队列尾部,并标记为独占模式。
  3. acquireQueued()方法:让线程在等待队列中获取获取资源,直到获取到资源才返回;如果等待过程中被中断过,则返回true,否则返回false。
  4. 3中线程在等待中被中断过,它是不响应。只是获取资源后才进行自我中断selfInterrupt(),设置中断标识。

tryAcquire()方法

/**
*尝试获取锁,由子类实现
*/
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

tryAcquire在AQS中是空方法,子类要重写该方法,这里没有定义成abstract方法,原因在于独占锁重写tryAcquire和tryRelease,共享锁重写tryAcquireShared和tryReleaseShared,如果定义成abstract抽象方法,则子类要实现四个方法。

addWaiter()方法

/**
*加入独占线程节点到队列尾上
*/
private Node addWaiter(Node mode) {
    //用当前线程创建一个Node节点
    Node node = new Node(Thread.currentThread(), mode);
    //先尝试快读加入队列,成功则返回新节点Node
    //失败,则采用自旋加入节点直至入队成功返回该节点
    Node pred = tail;
    //队尾非空
    if (pred != null) {
        //node节点的prev引用指向队尾节点
        node.prev = pred;
        //如果CAS入队尾成功
        if (compareAndSetTail(pred, node)) {
            //原队尾节点的next引用指向node
            pred.next = node;
            //返回node
            return node;
        }
    }
    //如果队尾为空
    //或者通过CAS进入队尾失败,存在竞争
    //通过enq()方法自旋
    enq(node);
    //返回node
    return node;
}

addWaiter()方法调用的enq()方法

/**
*自旋方式使node进入队尾
*/
private Node enq(final Node node) {
    //自旋
    for (;;) {
        //队尾节点
        Node t = tail;
        //如果队列为空
        if (t == null) { // Must initialize
            //创建head节点
            //CAS设置队列头节点
            if (compareAndSetHead(new Node()))
                //此时队列只有一个节点
                //头节点等于尾节点
                tail = head;
        } else {
            //设置node节点的prev引用指向t
            node.prev = t;
            //CAS设置新的队尾节点为node
            if (compareAndSetTail(t, node)) {
                //原队尾节点的next引用指向node
                //node就是新的尾节点tail
                t.next = node;
                //自旋结束,返回原尾节点
                return t;
            }
        }
    }
}

acquireQueued()方法

/**
*节点加入队列后,尝试在队列中自旋获取资源
*/
final boolean acquireQueued(final Node node, int arg) {
    //标记是否成功拿到资源
    boolean failed = true;
    try {
        //标记是否被中断
        boolean interrupted = false;
        //自旋
        for (;;) {
            //node的前驱节点,会抛出NullPointerException异常
            final Node p = node.predecessor();
            //如果node的前驱节点是队列头节点head
            //head节点释放资源后
            //紧接着就应该是node尝试获取资源
            //因此调用tryAcquire()尝试获取资源
            if (p == head && tryAcquire(arg)) {
                //如果获取资源成功
                //设置node为新的头结点
                setHead(node);
                //设置原头节点的后继节点next为null
                //帮助垃圾收集
                p.next = null;
                //标记已经获取到资源
                failed = false;
                //返回interrupted=false
                return interrupted;
            }
            //如果node的前驱节点不是队列头节点head
            //或者node节点尝试获取资源失败
            //进入此分支
            //1.检查并更新无法获取资源的节点的状态
            //如果线程应该阻塞,则返回true
            //2.阻塞线程,并检查中断状态
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //如果1、2条件都满足,需要设置中断标识为true
                interrupted = true;
        }
    } finally {
        //如果node前驱节点为空,抛出NullPointerException异常
        //说明node为头节点,此时failed为true
        if (failed)
            //取消对资源的获取
            cancelAcquire(node);
    }
}
/**
*获取节点的前驱节点,会抛出NullPointerException异常
*/
final Node predecessor() throws NullPointerException {
    //指向当前节点的前驱节点的引用
    Node p = prev;
    //如果前驱节点为空,抛出NullPointerException
    if (p == null)
        throw new NullPointerException();
    else
        return p;
}

shouldParkAfterFailedAcquire()方法

/**
*线程获取资源失败后,判断是否阻塞线程
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //获取前驱节点的状态
    //根据前驱节点的状态判断是否将线程阻塞
    int ws = pred.waitStatus;
    //如果前驱节点已经被设置为SIGNAL
    if (ws == Node.SIGNAL)
        /*
         * 前驱节点释放资源后,马上唤醒后继节点。返回true,表示阻塞线程
         */
        return true;
    //如果前驱节点状态大于0,即CANCELLED状态
    if (ws > 0) {
        /*
         * 前驱节点的线程被撤销,跳过所有的被撤销的prev节点
         * 将上一个状态小于0的节点设置为node的前驱节点
         */
        do {
            //如果waitStatus>0
            //修改node.prev指向前驱节点的前驱节点
            node.prev = pred = pred.prev;
        //while循环,waitStatus<0跳出循环
        } while (pred.waitStatus > 0);
        //退出while循环后,当前pred就是node的前驱节点
        //修改pred节点的后继节点为node
        pred.next = node;
    } else {
        /*
         * 如果上面条件都不满足,此时waitStatus一定是0或者PROPAGATE
         * 通过CAS设置前驱节点的状态为SIGNAL
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //返回false表示不阻塞线程
    //回到acquireQueued()方法,返回false会进入下一次自旋
    return false;
}

parkAndCheckInterrupt()方法

/**
*阻塞线程,判断线程是否中断
*/
private final boolean parkAndCheckInterrupt() {
    //park()会让当前线程进入waiting状态
    //在此状态下,有两种途径可以唤醒该线程
    //1.unpark()
    //2.interrupt()
    LockSupport.park(this);
    //返回线程是否被中断,会清除中断标识
    return Thread.interrupted();
}

如果parkAndCheckInterrupt()方法返回true,则acquireQueued()方法需要设置interrupt为true。acquireQueued()方法返回interrupted的状态。回到acquire()方法,如果acquireQueued()方法返回true,将会执行selfInterruput()方法。

selfInterruput()方法

/**
*中断线程,并不对中断做出响应
*/
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

小结

总结acquire()方法的流程。

  1. 尝试获取资源,如果获取资源成功,tryAcquire()方法返回true,则acquire()方法执行结束。否则进入步骤2。
  2. 如果尝试获取资源失败,tryAcquire()方法返回false,则执行addWaiter()方法将线程以节点的方法添加到队列的末尾。
  3. addWaiter()方法也许会存在竞争,造成入队尾失败的情况。需要通过自旋的方式入队尾。
  4. 入队尾成功后,通过acquireQueued()方法尝试在队列中获取资源或者阻塞线程。
  5. park()方法阻塞线程后,等待前驱节点调用unpark()方法或者线程中断唤醒线程。
  6. 判断线程是否中断并维护线程中断标识。

独占锁解锁

release()方法

/**
*释放独占的自旋
*会唤醒等待队列里的其他线程来获取资源
*/
public final boolean release(int arg) {
    //如果tryRelease()释放资源成功
    if (this.tryRelease(arg)) {
        //获取队列的头节点
        AbstractQueuedSynchronizer.Node h = this.head;
        //如果队列的头节点非空
        //并且头节点的waitStatus不等于0
        //则队列中可能存在待唤醒的节点
        if (h != null && h.waitStatus != 0) {
            //唤醒后继节点,让后继节点竞争资源
            this.unparkSuccessor(h);
        }
        //释放独占的资源成功,返回true
        return true;
    } else {
        //释放独占的资源失败,返回false
        return false;
    }
}

tryRelease()方法

/**
*释放资源,由子类实现完成
*/
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}

unparkSuccessor()方法

/**
*唤醒后继节点
*/
private void unparkSuccessor(AbstractQueuedSynchronizer.Node node) {
    //节点的状态,如果小于0则肯定不是CANCELLED状态
    int ws = node.waitStatus;
    if (ws < 0) {
        //CAS将节点状态修改为0
        node.compareAndSetWaitStatus(ws, 0);
    }
    //节点node的后继节点
    AbstractQueuedSynchronizer.Node s = node.next;
    //如果后继节点非空,且状态大于0,即CANCELLED
    //说明后继节点的线程取消对资源的等待
    if (s == null || s.waitStatus > 0) {
        //将后继节点置为null
        s = null;
        //从队列尾节点开始向前遍历
        //找到队列中node节点后第一个等待唤醒的节点
        //如果遍历到的节点t非空且不等于当前节点node
        //则校验节点p的状态
        for(AbstractQueuedSynchronizer.Node p = this.tail; p != node && p != null; p = p.prev) {
            //如果节点的状态小于等于0
            if (p.waitStatus <= 0) {
                //则将s指向p
                s = p;
            }
        }
    }
    //如果节点s非空
    if (s != null) {
        //唤醒节点s对应的线程
        LockSupport.unpark(s.thread);
    }
}

unparkSuccessor()方法执行流程

  1. 将node节点的状态设置为0;
  2. 寻找下一个非取消状态的节点p;
  3. 如果节点s不为null,则调用LockSupport.unpark(s.Thread)方法唤醒s所在线程。

注:唤醒线程也是有顺序的,就是添加到CLH队列线程的顺序。

小结

总结release()方法的流程

  1. 调用tryRelease()方法释放当前持有的锁资源;
  2. 如果完全释放了锁资源,那么就调用unparkSuccessor()方法,去唤醒一个等待锁的线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值