尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)

尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码 (一)


这篇文章希望通过对 AbstractQueuedSynchronizer 内部类 ConditionObject 的探索,加深对阻塞唤醒机制的理解!


一、await()方法流程

ReentrantLock lock = new ReentrantLock(true);
Condition con = lock.newCondition();
con.await();

这一段代码很常见,当调用await方法,阻塞当前线程。下面我们来看看具体实现:

AbstractQueuedSynchronizer.ConditionObject.await()

public final void await() throws InterruptedException {
        //手动检测当前线程是否中断,并清空中断标识
        //如果中断,则抛出中断异常
            if (Thread.interrupted())
                throw new InterruptedException();
            //创建一个节点并添加到一个队列中
            Node node = addConditionWaiter();
            //释放该节点持有的锁
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);//线程阻塞于此
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

加入condition队列

下面详细看下addConditionWaiter

AbstractQueuedSynchronizer.ConditionObject.addConditionWaiter()

private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

通过这段代码,其实不难发现:
1、每个condition有一个关联的队列,并且这个队列是个单向链表;
2、每个condition队列头节点为firstWaiter、尾节点为lastWaiter。使用nextWaiter 进行彼此之间的关联

这里写图片描述

到现在为止,我们知道了两个队列,同步队列(阻塞队列)和条件队列(condition队列),那么这两个队列之间有什么关联呢? 值得我们关注

unlinkCancelledWaiters方法用于清除队列状态不为-2的节点。

private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }

释放线程持有的锁

线程被阻塞时,同时需要放弃自己所持有的锁,并唤醒后继节点。最后将节点状态改为-2。在释放锁时,如果当前线程不是锁持有者,则抛出IllegalMonitorStateException异常。

final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
            //将当前节点状态修改为-2;当添加下一个节点时,该节点将会被清除
                node.waitStatus = Node.CANCELLED;
        }
    }

等待进入同步队列

//判断当前线程是否在同步队列中
final boolean isOnSyncQueue(Node node) {
    //如果节点状态为-2,或者无前驱节点,返回false
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        //如果next属性不为null,返回true
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         */
         // 从队尾遍历
        return findNodeFromTail(node);
    }

线程不断自旋来确认是否还在同步队列中,如果不在同步队列中了,则阻塞当前线程;原来await方法使用的就是park

二、signal()方法流程

使用该用法用于唤醒一个阻塞的线程,该方法只有锁持有者可以调用,否则抛出异常。

public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
private void doSignal(Node first) {
            do {
            //将firstWaiter指向第二个等待的节点
            //如果第二个等待的节点为null,让尾节点修改为null
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                //去掉与第二个等待节点的管理
                first.nextWaiter = null;
            }//如果转移失败,则循环转移下一个节点,直到成功
             while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
//将节点node转移到同步队列
final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         * 如果无法修改node节点是waitStatus,说明node节点的状态为1。
         * 进行条件队列下一个节点的转移
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        //进入阻塞队列尾部,p表示前驱节点
        Node p = enq(node);
        int ws = p.waitStatus;
        //如果前驱节点状态为1(已经取消),或者改变前驱节点的状态为-1失败,则直接唤醒node节点
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值