AQS条件对象

阅读更多

在AQS队列中通过nextWaiter指针串起来的就是条件队列,实际上是通过ConditionObject来实现的。ConditionObject类实现了Condition接口。Condition 实现可以提供不同于 Object 监视器方法的行为和语义。比如一个对象里面可以有多个Condition,可以注册在不同的condition,可以有选择性的调度线程,很灵活。而Synchronized只有一个condition(就是对象本身),所有的线程都注册在这个conditon身上,线程调度不灵活。

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。Condition 实例实质上被绑定到一个锁上。要为特定 Lock实例获得 Condition 实例,请使用其 newCondition() 方法。

条件变量在某个状态条件到达之前一直挂起该线程,由于多个线程都会访问这个共享状态信息,因此这个状态信息必须是线程安全的,这时就需要有锁来支撑。比如等待一个条件变量需要以原子方式释放获取的锁,并挂起当前线程,这和object.wait()类似。

以下是Condition接口的主要方法:

public interface Condition {
    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}

多个wait方法对应object.wait()方法,signal方法对应object.notify()方法,signalAll方法对应object.notifyAll()方法。下面分析每个方法的实现过程:

 await操作,前面说过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);
        }

 1、通过addConditionWaiter把当前线程加到条件队列

        /**
         * 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;
        }

a、如果条件队列队尾结点waitStatus不为CONDITION,证明该条件队列可能包含被取消的线程,unlinkCancelledWaiters操作正是踢掉这些waitStatus不为CONDITION的结点。

b、新建一个含有当前线程的结点放到条件队列尾部

c、返回新结点

2、然后是释放当前结点拥有的锁,不然会造成死锁,这是通过fullyRelease来实现的

    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;
        }
    }

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

 fullyRelease首先获取state,然后调用release方法释放锁,如果释放成功,直接返回,如果释放失败,证明当前结点存在异常,设置当前结点为CANCEL。

 release方法是调用tryRelease来实现的,tryRelease成功后需要唤醒在等待的线程,此处唤醒的是同步队列中的第一个非CANCEL结点。

3、自旋挂起当前线程,直到被唤醒,超时或者被CANCEL.这是通过while循环实现的,如果没有在同步队列中就会一直挂起。是否在同步队列是通过isOnSyncQueue判断的

final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        return findNodeFromTail(node);
   }

 显然waitStatus为CONDITION这种结点,只属于条件队列,不在同步队列中。

4、 获取锁,并从条件队列中移除,表示它已经拿到锁了。获取锁是通过acquireQueued实现的

    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);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 acquireQueued的过程如下:

a、如果当前结点的前一个结点是傀儡节点,就尝试获取锁,如果获取成功就设置头结点为当前结点。

b、到此表明还有其他结点在排除,当前就应该被挂起,挂起当前线程,设置中断标志,然后返回。

整个await就经过这几步(释放锁---加入条件队列----挂起---条件满足进入同步队列),其他await方法过程差不多,这里不再分析了。

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);
        }

  1、首先判断正在运行是不是当前线程,如果不是,则抛出异常!这是通过 isHeldExclusively来实现的,isHeldExclusively方法在AQS中是一个抽象方法,有多种实现,以下是在ReentrantLock中的实现:

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

 2、然后调用doSignal方法唤醒第一个线程

    private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

    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;
    }

 doSignal()方法是一个while循环,直到transferForSignal成功为止,transferForSignal做了两件事,1、将结点从条件队列移到同步队列中,2、设置同步中它的前趋结点的waitStatus为SIGNAL,并挂起当前线程。其实doSignal就是不断地完成这样一个transfer(条件队列--->同步队列)操作,直到有一个成功为止。

signalAll就是唤醒条件列表中的所有线程,实现很简单,循环调用signal方法即可。

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

/**
 * Removes and transfers all nodes.
 * @param first (non-null) the first node on condition queue
 */
private void doSignalAll(Node first) {
	lastWaiter = firstWaiter = null;
	do {
		Node next = first.nextWaiter;
		first.nextWaiter = null;
		transferForSignal(first);
		first = next;
	} while (first != null);
}
 

 

 

 

 

 

 

 

 

你可能感兴趣的:(AQS,CONDITION,条件队列,LOCK,锁)