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

来源:互联网 发布:香港网络歌手小背心 编辑:程序博客网 时间:2024/05/26 12:58

尝试阅读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
原创粉丝点击