java并发之 AQS相关组件Condition

什么是Condition

  • Condition是一个接口
  • 它提供了类似Object的监视器方法,配合Lock可以实现 等待/通知模式
  • 相比object的监视器方法
    • 它可以支持多个等待队列
    • 支持等待时中断
    • 支持超时等待
  • Condition对象的创建依赖于Lock对象
    • 在Lock的源码中我们可以看到Condition newCondition();获取 Condition的定义
  • Condition的实现是在AQS之中实现的

这里我们需要说一下,一个lock对象对应多个condition。
这里的含义相当于是对于同一个锁(资源)下的所有线程,它们之间有着不同的条件关系,所以需要我们分别来定义condition

Condition的使用及源码分析

前面介绍时说到,Condition是在Lock中定义的。
那么我们需要使用Condition时,则需要通过lock对象.newContion()方法来获取。

然后可以使用condition对象.await()方法,进入等待,同时释放锁。
然后其他对象通过调用condition对象.signal()方法唤醒等待的线程,同时释放锁。

Condition的实现

Condition是一个接口,它具体的实现是在AQS之中的,作为AQS的一个内部类存在。
这里的逻辑比较清晰,因为Condition是依赖于Lock存在的,而Lock又必须要使用AQS,所以这样安排子的话。Lock对象中就一定有AQS,也就一定有Condition了。

在AQS源码中,我们可以找到Condition的实现类ConditionObject:

public class ConditionObject implements Condition, java.io.Serializable 

Condition接口并没有定义队列,但是其实现类却是定义了等待队列。

  • 这是一个单向的队列
  • 维护它是用了firstWaiter,lastWaiter节点
  • 此处的节点是复用了AQS中同步队列的节点(所以每个节点中都有线程引用)
  • 每一个Condition对象都有一个等待队列
  • 一个锁对象可以有多个Condition对象,所以一个锁对象有一个同步队列和多个Condition等待队列
    在这里插入图片描述

下面来将一下Condition等待、等待队列、通知的流程:

等待:

  • 首先需要明确,与wait()一样,能够执行等待与通知的线程都是获取了锁(or同步状态)的线程,所以这里是没有同步问题担心的,由锁帮我们解决了线程安全问题
  • 当一个线程执行了await()方法,它会释放锁,然后构造节点加入到等待队列的队尾,进入等待状态。
  • 然后它就在队列里呆着,一直呆着,直到有人signal,唤醒等待队列,将等待队列的firstWaiter(节点)加入到同步队列的队尾。直到自己也进入同步队列为止

通知:

  • 与await()一样,执行signal方法的线程也是获取了锁
  • 它释放锁,然后等待队列的firstWaiter移动到同步队列中去,然后唤醒该节点。

下面来看一下源码:
首先是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);
        }

看我在代码中的注释

扫描二维码关注公众号,回复: 12692696 查看本文章
	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(Node.CONDITION);

            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

这里是创建一个节点,节点的mode是Condition,然后将其插入到等待队列的尾部

	final int fullyRelease(Node node) {
    
    
        try {
    
    
            int savedState = getState();
            if (release(savedState))
                return savedState;
            throw new IllegalMonitorStateException();
        } catch (Throwable t) {
    
    
            node.waitStatus = Node.CANCELLED;
            throw t;
        }
    }

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

可以看到这里释放同步状态,实际需要调用AQS的tryRelease方法,来释放同步状态。

之后便调用LockSupport的方法阻塞线程。

然后来看signal方法:

	public final void signal() {
    
    
        if (!isHeldExclusively())
             throw new IllegalMonitorStateException();
         Node first = firstWaiter;
         if (first != null)
             doSignal(first);//这里
      }
	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 cannot change waitStatus, the node has been cancelled.
         */
        if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);//进入同步队列
        int ws = p.waitStatus;
        if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);//唤醒
        return true;
    }

这里可以看到,signal时,唤醒的对象是firstWaiter。

之后一层层的调用,先将该节点通过enq函数加入同步队列,然后
将firstWaiter的线程用LockSupport的unpark方法进行唤醒。

猜你喜欢

转载自blog.csdn.net/qq_34687559/article/details/114287156