jdk源码解读-并发包-Lock-ReentrantLock(2)--await()与signal()方法走读

来源:互联网 发布:乐高编程机器人难不难 编辑:程序博客网 时间:2024/06/18 00:51

本人知乎技术文章

ReentrantLock 的基本操作除了lock()和unlock()外,还有condition的await()和signal()。但是是通过调用AbstractQueuedSynchronizer的内部类CondtionObject来实现的。所以await()和singnal()的操作主要在CondtionObject类里。

如上图可以看到,ConditonObject是AbstractQueuedSynchronizer的内部类,同时上节讲解lock()和unlock()提到的Node也是它的内部类,以及其他方法组成了依赖FIFO waiting queue的阻塞锁和相关同步器(semaphores,events,等待)基本框架。

现在主要看一下内部类ConditionObject:

/** * Condition implementation for a {@link * AbstractQueuedSynchronizer} serving as the basis of a {@link * Lock} implementation. * * <p>Method documentation for this class describes mechanics, * not behavioral specifications from the point of view of Lock * and Condition users. Exported versions of this class will in * general need to be accompanied by documentation describing * condition semantics that rely on those of the associated * {@code AbstractQueuedSynchronizer}. * * <p>This class is Serializable, but all fields are transient, * so deserialized conditions have no waiters. */public class ConditionObject implements Condition, java.io.Serializable {
一. await操作的流程:
因为线程是先通过lock()获得锁资源,然后调用await()时,先释放锁资源然后park。所以park前要先释放锁,让别的线程获得争取锁资源的权利。
调用流程图:

1.await()

/** * Implements interruptible condition wait. * <ol> * <li> If current thread is interrupted, throw InterruptedException. * <li> Save lock state returned by {@link #getState}. * <li> Invoke {@link #release} with saved state as argument, *      throwing IllegalMonitorStateException if it fails. * <li> Block until signalled or interrupted. * <li> Reacquire by invoking specialized version of *      {@link #acquire} with saved state as argument. * <li> If interrupted while blocked in step 4, throw InterruptedException. * </ol> */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);}

首先用addConditionWaiter()方法把这个线程包装成Node并放入condition queue。

2.addConditionWaiter():加入condition等待队列。

/** * Adds a new waiter to wait queue. * @return its new wait node */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;}

新建包装当前线程的node。找到condition队列中没有被cancel的lastWaiter,然后把当前Node设为lastWaiter。

3.fullyRelease(node):释放当前state值。并返回释放前的值。

/** * Invokes release with current state value; returns saved state. * Cancels node and throws exception on failure. * @param node the condition node for this wait * @return previous sync state */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)            node.waitStatus = Node.CANCELLED;    }}

4.release(savedState):tryRelease(arg)调用释放锁资源:

/** * Releases in exclusive mode.  Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument.  This value is conveyed to *        {@link #tryRelease} but is otherwise uninterpreted and *        can represent anything you like. * @return the value returned from {@link #tryRelease} */public final boolean release(int arg) {if (tryRelease(arg)) {        Node h = head;if (h != null && h.waitStatus != 0)            unparkSuccessor(h);return true;    }return false;}

5.unparkSuccessor(h):叫醒syn queue中沉睡的线程:找到头结点后第一个没有被cancel的节点,然后对这个节点包装的线程执行unpark()操作

 /*** Wakes up node's successor, if one exists. * * @param node the node */private void unparkSuccessor(Node node) {/*     * If status is negative (i.e., possibly needing signal) try     * to clear in anticipation of signalling.  It is OK if this     * fails or if status is changed by waiting thread.     */    int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);/*     * Thread to unpark is held in successor, which is normally     * just the next node.  But if cancelled or apparently null,     * traverse backwards from tail to find the actual     * non-cancelled successor.     */    Node s = node.next;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);}

await()操作总结:

1.新建包装当前线程的node放到condition waiting queue中去。

2.释放此节点的锁资源。

3.找到syn queue中头节点后第一个没被cancel的节点并叫醒这个节点沉睡的线程。

4.对当前线程进行park();

二. signal():首先这个线程获取到了锁资源,然后执行signal()操作,把condition queue 中的第一个节点删除,并把这个节点放入syn queue 。

调用图:


1.signal():

/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}

对condition queue 第一个节点进行唤醒。

2.doSignal(first):从队头找,找到第一个非空节点进行节点迁移。

/**
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}


3.transferForSignal(Node node):把当前节点从condition queue 迁移到sync queue 中。

final boolean transferForSignal(Node node) {

if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}

先调用enq(node);把节点放到sync queue 的队尾,同时unpark 当前线程,这时这个线程又获得了竞争锁资源的资格。
0 0
原创粉丝点击