AQS--条件队列(待整理)

//条件队列属性: 只有两个属性 firstWaiter 和 lastWaiter;
 public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;

        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

        public ConditionObject() { }
}

条件队列和堵塞队列的关系:

  • 每个 condition 有一个关联的条件队列,条件队列是一个单向链表;
  • 调用condition1.signal() 触发一次唤醒,此时唤醒的是队头,会将 firstWaiter(队头) 移到阻塞队列的队尾,等待获取锁,获取锁后 await 方法才能返回
 private static final Lock lock = new ReentrantLock();
    private static final Condition fullCondition = lock.newCondition();  //队列满的条件
    private static final Condition emptyCondition = lock.newCondition(); //队列空的条件



    /**
     * 生产者
     */
    public static class Producer extends Thread{
        @Override
        public void run(){
            while(true){
                //获得锁
                lock.lock();
                while(queue.size() == maxSize){
                    try {

                        //条件不满足,生产阻塞
                        fullCondition.await();
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }

                queue.offer(i++);

                //唤醒其他所有生产者、消费者
                fullCondition.signalAll();
                emptyCondition.signalAll();

                //释放锁
                lock.unlock();

                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    /**
     * 消费者
     */
    public static class Consumer extends Thread{


        @Override
        public void run(){
            while(true){
                //获得锁
                lock.lock();
                while(queue.isEmpty()){
                    try {

                        //条件不满足,消费阻塞
                        emptyCondition.await();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                int x = queue.poll();
               

                //唤醒其他所有生产者、消费者
                fullCondition.signalAll();
                emptyCondition.signalAll();

                //释放锁
                lock.unlock();

                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

按照流程分析:

await:

//直到被通知或者中断否则一直阻塞
public void await() throws InterruptedException {
	if (Thread.interrupted())
		throw new InterruptedException();
	//将该线程添加到CONDITION队列中
	Node node = addConditionWaiter();
	//该节点加入condition队列中等待,await则需要释放掉当前线程占有的锁
	int savedState = fullyRelease(node);
	int interruptMode = 0;

	//出了while循环,代表线程中断/已经将该node从CONDITION队列transfer到了CLH队列中
	//判断该节点是否在CLH队列中
	while (!isOnSyncQueue(node)) {
		//不在,则阻塞该节点
// 有以下情况会让 LockSupport.park(this); 这句返回继续往下执行:
//
//常规路径。signal -> 转移节点到阻塞队列 -> 获取了锁(unpark)
//线程中断。在 park 的时候,另外一个线程对这个线程进行了中断
//signal 的时候我们说过,转移以后的前驱节点取消了,或者对前驱节点的CAS操作失败了
//假唤醒。这个也是存在的,和 Object.wait() 类似,都有这个问题
		LockSupport.park(this);
		//在阻塞的过程中发生中断
		if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
			break;
	}

	//acquireQueued在队列中获取锁,会阻塞当前线程,
	//并且在上面while循环等待的过程中没有发生异常,则修改interruptMode状态为REINTERRUPT
	if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
		interruptMode = REINTERRUPT;
	//该节点在阻塞的过程中发生中断,会加入阻塞队列,
	//此时该节点的nextWaiter不为null,需要调用unlinkCancelledWaiters将该节点从CONDITION队列中删除,该节点的状态为0
	if (node.nextWaiter != null) // clean up if cancelled
		unlinkCancelledWaiters();
	//如果interruptMode不为0,则代表该线程在上面过程中发生了中断或者抛出了异常,则调用reportInterruptAfterWait方法在此处抛出异常
	if (interruptMode != 0)
		reportInterruptAfterWait(interruptMode);
}

1.如果线程中断,抛出异常InterruptedException;

2.将该线程添加到CONDITION队列的队尾

        //首先检查尾节点是否为cancelled状态的节点,如果是则调用unlinkCancelledWaiters删除CONDITION队列中所有cancelled状态的节点,不是,则直接将该新创建的节点添加到CONDITION队列的末尾。
        private Node addConditionWaiter() {
            //尾指针
            Node t = lastWaiter;
            //如果尾节点状态是cancelled,则调用unlinkCancelledWaiters方法删除CONDITION链表中所有cancelled状态的节点
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                //t为新的尾节点
                t = lastWaiter;
            }
            //创建一个node节点,状态为CONDITION
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            //添加到队尾
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }
        //遍历一次CONDITION链表,删除状态为CANCELLED的节点。
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                //下一个节点
                Node next = t.nextWaiter;
                //如果t的状态是cancelled的,则需要删除t
                if (t.waitStatus != Node.CONDITION) {
                    //清除t的nextWaiter连接
                    t.nextWaiter = null;
                    //删除的是首节点
                    if (trail == null)
                        firstWaiter = next;
                    else
                        //直接将前一个节点的连接指向该节点的下一个节点
                        trail.nextWaiter = next;
                    //设置新的尾节点
                    if (next == null)
                        lastWaiter = trail;
                }
                //状态为CONDITION的节点不需要清除
                else
                    trail = t;
                t = next;
            }
        }

3.该节点加入condition队列中等待,await则需要释放掉当前线程占有的锁

 //完全释放锁,释放成功则返回,失败则将当前节点的状态设置成cancelled表示当前节点失效
        final int fullyRelease(Node node) {
            boolean failed = true;
            try {
                //获取当前锁重入的次数
                int savedState = getState();
                //释放锁
                if (release(savedState)) {
                    //释放成功
                    failed = false;
                    return savedState;
                } else {
                    throw new IllegalMonitorStateException();
                }
            } finally {
                //释放锁失败,则当前节点的状态变为cancelled(此时该节点在CONDITION队列中)
                if (failed)
                    node.waitStatus = Node.CANCELLED;
            }
        }
//尝试释放锁,释放成功则调用unparkSuccessor唤醒后继节点
    public final boolean release(int arg) {
        //调用tryRelease释放锁。
        if (tryRelease(arg)) {
            //释放成功,则查看head节点状态,如果不为null且状态不为0(为0表示没有后继或者当前节点已经unparkSuccessor过),则调用unparkSuccessor唤醒后继节点
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //完全释放
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

4.线程中断/已经将该node从CONDITION队列transfer到了CLH队列中,才出while循环(中断:本来有个线程,它是排在条件队列的后面的,但是因为它被中断了,那么它会被唤醒,然后它发现自己不是被 signal 的那个,但是它会自己主动去进入到阻塞队列)

//判断该节点是否在CLH队列中
final boolean isOnSyncQueue(Node node) {
//如果该节点的状态为CONDITION(该状态只能在CONDITION队列中出现,CLH队列中不会出现CONDITION状态),或者该节点的prev指针为null,则该节点一定不在CLH队列中
	if (node.waitStatus == Node.CONDITION || node.prev == null)
		return false;
	//如果该节点的next(不是nextWaiter,next指针在CLH队列中指向下一个节点)状态不为null,则该节点一定在CLH队列中
	if (node.next != null) // If has successor, it must be on queue
		return true;
	//否则只能遍历CLH队列(从尾节点开始遍历)查找该节点
		return findNodeFromTail(node);
}
	//从尾节点开始,使用prev指针,遍历整个CLH队列
private boolean findNodeFromTail(Node node) {
	Node t = tail;
	//从尾节点开始,使用prev指针,开始遍历整个CLH队列
	for (;;) {
	//找到该节点
		if (t == node)
			return true;
		//遍历完成,没有找到该节点
		if (t == null)
			return false;
		t = t.prev;
	}
}

检查线程中断标志:
0:没有发生中断
THROW_IE=-1:通知之前发生中断
REINTERRUPT=1:异常发生在通知中断之后返回重新中断的标志

private int checkInterruptWhileWaiting(Node node) {
	return Thread.interrupted() ?
		(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
		0;
}


//是否在 await() 期间发生了中断,因为之前的状态等于0,
final boolean transferAfterCancelledWait(Node node) {
	if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
	//加入阻塞队列
	enq(node);
	return true;
	}
}

5.获取独占锁

此时节点已经进入了阻塞队列,准备获取锁。

   //acquireQueued在队列中尝试去获取锁,返回值就是代表线程是否被中断。如果返回 true,说明被中断了,而且 interruptMode != THROW_IE,说明在 signal 之前就发生中断了
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    interruptMode = REINTERRUPT;
//该节点调用transferAfterCancelledWait添加到CLH队列中的,此时该节点的nextWaiter不为null,需要调用unlinkCancelledWaiters将该节点从CONDITION队列中删除,该节点的状态为0
if (node.nextWaiter != null) // clean up if cancelled
    unlinkCancelledWaiters();
//如果interruptMode不为0,则代表该线程在上面过程中发生了中断或者抛出了异常,则调用reportInterruptAfterWait方法在此处抛出异常
 if (interruptMode != 0)
    reportInterruptAfterWait(interruptMode);

 

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页