Java多线程--AQS源码解析

获取独占标志位

    public final void acquire(int arg) {
		//尝试获取锁失败后,当前请求构造node节点添加到同步队列中,然后自旋逻辑锁
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

获取独占锁的核心逻辑

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //开始自旋
            for (;;) {
                final Node p = node.predecessor();
                //如果当前请求的前置节点为头节点,开始尝试获取锁,
                if (p == head && tryAcquire(arg)) {
                    //获取锁成功-》设置当前节点为头节点
                    setHead(node);
                    //之前的头节点断开后续节点,方便gc回收。
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
				//如果前置节点waitStatus状态为SIGNAL,那么线程处理等待状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire 判断当前线程是否需要陷入等待

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            //如果前置节点waitStatus状态为signal,标识前置节点释放时会唤醒当前节点,那么就陷入等待,等待前置节点唤醒即可
            return true;
        if (ws > 0) {
            //如果前置节点为退出的状态,那么就将这些节点断开,继续自旋,
            //下一次继续执行shouldParkAfterFailedAcquire判断方法,
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //如果节点为0、PROPAGATE,那么就将前置节点置为SIGNAL,继续自旋。
            //下一次继续执行shouldParkAfterFailedAcquire判断方法,
            //下一次进入的话,判断为SIGNAL,那么说明前面的节点释放时会唤醒这个节点。
            //waitStatus为0,可能是获取到了同步标识位,没有设置node节点到队列中
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

主线逻辑:尝试获取独占标识位失败-》当前请求添加到同步队列的尾部-》自旋获取锁-》当前节点前置节点为头节点,尝试获取锁-》

判断头节点后面是否有节点在等待

    public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        //(s = h.next) == null head和tail有可能刚刚完成初始化,tail还没指向新添加的节点。
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

释放独占锁

    public final boolean release(int arg) {
        //尝试释放锁
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //唤醒后续节点
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

主线逻辑:释放同步标志为-》唤醒后续节点。

获取共享标志位
核心代码

    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) {
                    //前置节点位头节点,尝试获取锁
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //设置头节点-》唤醒后续节点去获取共享锁
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //如下和共享锁的中逻辑一样
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

主线逻辑:构造共享节点-》进入自旋-》如果前置节点为头节点-》尝试获取共享状态-》设置当前节点为头节点-》设置头节点waitStaus状态为0-》唤醒后续节点

释放共享节点
doReleaseShared

private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //如下是acquireShared调用的。
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //唤醒后续节点
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    //理论上头节点的waitStatus应该为SIGNAL,因为后续节点执行shouldParkAfterFailedAcquire将前置节点置为SIGNAL。
                    //这里ws==0说明头节点是刚刚成为的,然后立刻新来了一个节点,还没有执行完shouldParkAfterFailedAcquire方法。
                    //!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)为true,说明头节点的waitStatu改变了。即后续节点执行了
                    //shouldParkAfterFailedAcquire方法,waitStatus应该为SIGNAL了。所以继续循环。
                    //!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)为false,后续节点还没执行shouldParkAfterFailedAcquire,
                    //头节点当前为PROPAGATE,那么后续节点执行shouldParkAfterFailedAcquire将后续节点waitStatus状态改为SIGNAL。
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

主线逻辑:释放共享标志位成功-》唤醒后续节点,
参考:https://segmentfault.com/a/1190000016447307

-------------------------------------------------------条件等待-----------------------------------------------------------
线程陷入等待状态,等待唤醒

        public final long awaitNanos(long nanosTimeout)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            // 将当前线程添加到条件队列中,waitStatus为CONDITION
            Node node = addConditionWaiter();
            // 释放独占锁
            int savedState = fullyRelease(node);
            final long deadline = System.nanoTime() + nanosTimeout;
            int interruptMode = 0;
            // 当前节点是否在同步队列中
            while (!isOnSyncQueue(node)) {
                if (nanosTimeout <= 0L) {
                    //等待时间到了,将当前节点同步到同步队列中
                    transferAfterCancelledWait(node);
                    break;
                }
                if (nanosTimeout >= spinForTimeoutThreshold)
                    //陷入等待
                    LockSupport.parkNanos(this, nanosTimeout);
                //检查是否中断,如果存在中断,且是在唤醒之前,表示向外抛中断异常,如果是在唤醒后设置的中断,
                //则传递中断标识
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
                nanosTimeout = deadline - System.nanoTime();
            }
            //自旋获取锁
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null)
                //移除条件队列中已经退出的节点。即waitStatus不等于-2的节点。
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                //向外抛异常或者设置中断标识。
                reportInterruptAfterWait(interruptMode);
            return deadline - System.nanoTime();
        }

关于异常的方法

    final boolean transferAfterCancelledWait(Node node) {

        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            //修改标志位成功,说明是在获取信号之前唤醒的。有可能是执行了中断。
            enq(node);
            return true;
        }
        /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
         */
         //执行上面的cas失败,那就是在被唤醒之后执行的,唤醒线程马上就会把节点添加到同步队列中,等待一下即可。
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }

等待主线逻辑:添加节点到条件队列-》是否锁-》检查是否在同步队列中,陷入等待,等待唤醒-》重新获取锁。
唤醒到该节点后会将节点同步到同步队列,所以这边就是检查当前节点是否被同步到了同步队列中,如果同步到了同步队列,则开始获取同步锁。

唤醒

public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

唤醒的核心代码

    final boolean transferForSignal(Node node) {
		//CAS失败,说明该节点的线程已经退出了。
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
		//将该节点从条件队列移动到同步队列
        Node p = enq(node);
        
        int ws = p.waitStatus;
        //前置节点已经退出,或者前置节点设置Signal标志异常,那么唤醒等待的线程
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

唤醒方法做了三件主要的事情
1 修改waitStatus状态为0
2 将节点从条件队列移动到同步队列
3 如果前置节点已经退出,或者前置节点设置Signal标志异常,那么唤醒等待的线程。
未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值