java并发-线程通讯之Condition源码分析

370 阅读4分钟

Condition介绍

1.Condition实现类ConditionObject是AQS中的内部类
2.Condition是依赖lock的,因为Condition中等待队列中的数据流向AQS等待队列的过程
3.每一个lock可以拥有多个condition
4.condition await signal/signalAll 和 synchronized+wait+notify/notifyAll功能比较类似,实现的逻辑也
比较类似;
当使用synchronized的wait方法,会将当前线程放入waitSet(睡眠的等待队列,不参与锁的竞争),
相当于使用condition调用await方法,将当前线程放入condition自己维护的队列中
当使用synchronized的方法notify方法会将waitSet中随机的一个线程移动到enterList(等待队列,参与锁的竞争)
相当于使用condition的signal方法,将队列中第一个元素移动到Lock的等待队列(AQS的等待队列,有机会参与锁的竞争)中去

代码示例

class BoundedContainer{
	
	private String[] elements = new String[10];
	
	private Lock lock = new ReentrantLock();
	
	private Condition notEmptyCondition = lock.newCondition();
	
	private Condition notFullCondition = lock.newCondition();
	
	private int elementCount;
	
	private int putIndex;
	
	private int takeIndex;
	
	
	public void put(String str) {
		
		try {
			lock.lock();
			while(elementCount==elements.length) {
				notFullCondition.await();
			}
			elements[putIndex] = str;
			if(++ putIndex == this.elements.length) {
				putIndex = 0;
			}
			++ elementCount;
			System.out.println("put method: "+Arrays.toString(this.elements));
			
			notEmptyCondition.signal();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		
		
		
	}
	
	
	public String take() {
		
		
		try {
			lock.lock();
			while(elementCount==0) {
				notEmptyCondition.await();
			}
			String element = elements[takeIndex];
			elements[takeIndex] = null;
			
			if(++ takeIndex == this.elements.length) {
				takeIndex = 0;
			}
			
			System.out.println("take method: "+Arrays.toString(this.elements));
			
			notFullCondition.signal();
			
			-- elementCount;
			return element;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		return null;
		
	}
	
	
}

源码分析

ConditionObject包含的成员变量

//内部就维护了一个双向列表,这个node 就是 AQS 中的node,是个双向链表
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

获取方法newCondition

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

//newCondition
public Condition newCondition() {
	//调用同步器的 newCondition 方法
    return sync.newCondition();
}

final ConditionObject newCondition() {
	//直接 new 了一个 ConditionObject 的实例,由此可见 lock 每次调用 newCondition 方法,都是获取到不同的 Condition 实例
    return new ConditionObject();
}

Condition.await()

public final void await() throws InterruptedException {
	//线程中断
    if (Thread.interrupted())
        throw new InterruptedException();
    //构建一个 node,并且把这个node 放入到 condition 自己的等待队列中去  
    Node node = addConditionWaiter();
    //因为要是使用condition的方法,必须要获取到lock锁,所以当 当前线程进入condition的等待队列
    //当前线程要释放持有的锁,这个方法里面主要就是调用了AQS 的 release 方法
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 这个 判断条件,有一个是判断 node 的 waitStatus状态是否为 Node.CONDITION
    //因为上面创建node的时候 就是赋值的 CONDITION,所以可以进入  while 代码块
    while (!isOnSyncQueue(node)) {
    	// LockSupport 是个基础工具类,主要使用的是 unsafe 类,unsafe 中还封装了 CAS 操作
        //park 方法的作用就是 让当前线程 陷入等待,不会参与cpu时间片的竞争,是底层c++ 方法 
         LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // acquireQueued 这个方法 是 AQS 中的方法,主要作用就是使当前线程在一个 for(;;) 中持续的去获取锁
    // 解释下  :  当一个线程被挂起,那么就算是唤醒了 也有去争夺锁,  lock.lock 方法中也是调用了这个方法
    // 假如不去争取锁,唤醒了有个篮子用
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

Condition.signal

public final void signal() {
	//判断下 当前线程是不是获取到锁的线程  ,  假如不是直接抛出异常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //获取到 condition 等待队列的第一个 节点
    Node first = firstWaiter;
    if (first != null)
    	// 
        doSignal(first);
}


private void doSignal(Node first) {
    do {
    	//如果 没有下一个了 直接把这个队列设置为 null
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
        //主要看下 transferForSignal 这个方法
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
     // 通过CAS 操作 设置这个node的状态 
    if (!compareAndSetWaitStatus(node, 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).
     */
     // 把 condition 等待队列第一个节点放置到 AQS 的等待队列中去等待去竞争锁
    Node p = enq(node);
    int ws = p.waitStatus;
    //这个判断 这个condition.signal 调用的过程中我感觉是不能进入的,但是代码存在是有原因 的 我在看看
   //按理说 这个被放入AQS等待队列中的线程  的唤醒的时机是在 当别的线程释放了锁的时候
   //这里应该是主要是为了 设置  node 的 waitStatus 的状态为 SIGNAL
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}