AQS简介(2) - 方法

enq 结点入队

//结点入队,必要的时候需要初始化
private Node enq(final Node node) {
    	//死循环;直到成功入队
        for (;;) {
            //获取同步队列的尾结点
            Node t = tail;
            //若尾结点为空,则初始化
            if (t == null) {
                //以CAS 模式设置头结点
                if (compareAndSetHead(new Node()))
                    //首尾相连
                    tail = head;
            } else {
                //若队列已经初始化,则将要要新结点的前继结点设置成旧的尾结点
                node.prev = t;
                //CAS设置尾结点
                if (compareAndSetTail(t, node)) {
                    //设置旧尾结点的后继引用为新尾结点
                    t.next = node;
                    //返回尾结点;循环唯一出口
                    return t;
                }
            }
        }
    }

addWaiter 添加结点

/**
 * 当前线程或给定模式的结点入队
 */
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 尝试快速入队,失败了则走 enq 方法
    	//获取尾结点
        Node pred = tail;
        if (pred != null) {
            //设置旧尾结点为新结点的前继结点
            node.prev = pred;
            //CAS 设置尾结点
            if (compareAndSetTail(pred, node)) {
                //设置旧尾结点的后继引用为新结点
                pred.next = node;
                //返回入队的结点
                return node;
            }
        }
    	//结点入队,具体看上面
        enq(node);
    	//返回入队的结点
        return node;
    }

unparkSuccessor 唤醒后继结点

/**
 * 唤醒后继(如果存在的话)
 */
private void unparkSuccessor(Node node) {
    //获取当前结点的等待状态;如果小于0,则 CAS 更新成 0
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    //获取当前结点的后继引用
    Node s = node.next;
    //后继引用为 null 或 被取消(大于0 就是取消了)
    if (s == null || s.waitStatus > 0) {
        //置空后继引用
        s = null;
        //从尾部向前推进获取第一个没有取消的结点
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //唤醒线程
        LockSupport.unpark(s.thread);
}

setHeadAndPropagate 设置头结点和后继结点状态

/**
 * 设置头结点并且后继引用可直接获取锁
 */
private void setHeadAndPropagate(Node node, int propagate) {
    //记录头结点的引用
    Node h = head; 
    //将当前结点设置成头结点
    setHead(node);
    //看不懂大师为啥这么判断 
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        //获取当前结点的后继引用
        Node s = node.next;
        if (s == null || s.isShared())
            //共享模式释放锁
            doReleaseShared();
    }
}

cancelAcquire 取消尝试

/**
 * 取消正在尝试获取锁的线程
 */
private void cancelAcquire(Node node) {
    if (node == null)
        return;

    node.thread = null;

    // 获取当前结点的前继结点(备注:前继1号,不然后面注释不好写),忽略取消状态的结点
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    // 获取 前继1号 的后继引用,可能是取消状态的结点引用
    Node predNext = pred.next;

    // 设置当前结点的状态为 取消 状态
    node.waitStatus = Node.CANCELLED;

    // 若当前结点是尾结点则自我毁灭,并且CAS设置前继1号为 尾结点
    if (node == tail && compareAndSetTail(node, pred)) {
        // CAS设置前继1号的后继引用为 null
        compareAndSetNext(pred, predNext, null);
    } else {
        int ws;
        if (pred != head 
            //设置前继1号的状态为 唤醒后继引用
            && ((ws = pred.waitStatus) == Node.SIGNAL ||(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) 
            && pred.thread != null) {
            //获取当前结点的后继引用
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                //CAS 设置 前继1号的后继引用
                compareAndSetNext(pred, predNext, next);
        } else {
            //猜想:走到这一步,前继加点可能是头结点
            //唤醒线程
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

shouldParkAfterFailedAcquire

/**
 * 字面直接翻译下:
 * 检查并更新无法获取的节点的状态。如果线程应该阻塞,则返回true。
 * 这是所有采集回路中的主要信号控制。要求 pred == node.prev
 * 
 * 简单说下:判断是否可以挂起当前线程
 * @param pred 前继结点的状态
 * @param node 结点
 * @return 如果线程阻塞返回 true,反之 false
 */
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        // 可以直接唤醒后继引用,所以放心吧
        return true;
    if (ws > 0) {
        // 过滤取消的结点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //waitStatus 可能是 0 或者 -3;无论是哪个都可以选择 置成 唤醒后继结点 状态
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

selfInterrupt 自我中断

/**
 * 自我中断(吓死爹了,是个狠角色)
 */
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

parkAndCheckInterrupt

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

acquireQueued

/**
 * 以 独占 不中断模式获取锁
 * 用于 获取 和条件等待队列方法 中
 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取参数 node 的前一个结点
            final Node p = node.predecessor();
            //判断是否是第一个节点
            // tryAcquire:独占方式获取,后面重点说
            if (p == head && tryAcquire(arg)) {
                // 设置头结点
                setHead(node);
                // 将上一个head结点的后继清空,为了帮助垃圾收集
                p.next = null; 
                failed = false;
                //返回中断的状态, 整个循环执行到这里才是出口
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                //判断是否可以挂起并且当前线程阻塞
                interrupted = true;
        }
    } finally {
        //确保如果失败就取消获取
        if (failed)
            cancelAcquire(node);
    }
}

doAcquireInterruptibly 中断的方式获取锁

/**
 * 可中断的方式获取锁
 * 大部分代码和上面  acquireQueued  方法类似,所以只在不一样的地方加注释
 */
private void doAcquireInterruptibly(int arg) throws InterruptedException {
    //包装当前线程成独占模式结点入队列
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            //tryAcquire:独占方式获取,后面重点说
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; 
                failed = false;
                // 获取锁成功后直接返回
                return;
            }
            // 线程被唤醒后如果发现中断请求就抛出异常
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

doAcquireNanos 超时获取锁

/**
 * 以限定超时时间模式获取锁
 * 大部分代码和上面  acquireQueued  方法类似,所以只在不一样的地方加注释
 */
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    //时间到了,自行解决
    if (nanosTimeout <= 0L)
            return false;
    //获取超时最终时间
    final long deadline = System.nanoTime() + nanosTimeout;
    //以独占模式添加结点入队列
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            //tryAcquire:独占方式获取,后面重点说
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null;
                failed = false;
                return true;
            }
            //看时间是否到了,超时时间到了就返回
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0) {
                return false;
            }
            //判断是否可以挂起
            //超时时间大于自旋时间;spinForTimeoutThreshold:自旋超时阈值,默认 1000
            if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) {
                //将当前线程挂起一段时间, 之后再自己醒来
                LockSupport.parkNanos(this, nanosTimeout);
            }
            //在获取锁的期间收到中断请求就抛出异常
            if (Thread.interrupted()) 
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

doAcquireShared 不中断共享锁

/**
 * 以 共享 不中断模式获取锁
 * 注意和 acquireQueued 的区别
 * 上面有的注释就不在写了,原谅我偷会懒,(*^▽^*)
 */
private void doAcquireShared(int arg) {
    //将当前线程以共享模式包装进队列
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                //tryAcquireShared:共享的方式获取,后面重点说
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; 
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

release 释放锁

//释放锁
public final boolean release(int arg) {
    //尝试释放锁
    if (tryRelease(arg)) {
        //获取头结点
        Node h = head;
        //头结点不为空并且等待状态不等于0就去唤醒后继引用
        if (h != null && h.waitStatus != 0) {
            //唤醒后继引用
            unparkSuccessor(h);
        }
        return true;
    }
    return false;
}

/**
 * 以共享的模式释放锁
 */
private void doReleaseShared() {
    for (;;) {
        //获取头结点引用
        Node h = head;
        //判断队列是否已经初始化了
        if (h != null && h != tail) {
            //获取头结点引用的状态,若是 唤醒后继引用 状态,便将状态置为 0 
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            
                // 醒来吧,沉睡的战士
                unparkSuccessor(h);
            } 
            //若头结点的转态是 0 ,则设置状态为 后继引用 可直接获取锁
            else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                
        }
        if (h == head)                   
            //结束循环
            break;
    }
}

还有一些方法没有列出,大部分都能在上面注释中找到对应的解释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值