显示条件1、用法2、生产者/消费者模式3、实现原理1、用法锁用于解决竞态条件问题条件是线程间的协作机制。显式锁与synchronized相对应而显式条件与wait/notify相对应。wait/notify与synchronized配合使用显式条件与显式锁配合使用。条件与锁相关联创建条件变量需要通过显式锁Lock接口定义了创建方法ConditionnewCondition();Condition表示条件变量是一个接口它的定义为publicinterfaceCondition{voidawait()throwsInterruptedException;voidawaitUninterruptibly();longawaitNanos(longnanosTimeout)throwsInterruptedException;booleanawait(longtime,TimeUnitunit)throwsInterruptedException;booleanawaitUntil(Datedeadline)throwsInterruptedException;voidsignal();voidsignalAll();}await对应于Object的wait, signal对应于notify,signalAll对应于notifyAll语义也是一样的。与Object的wait方法类似await也有几个限定等待时间的方法但功能更多一些//等待时间是相对时间如果由于等待超时返回返回值为false否则为truebooleanawait(longtime,TimeUnitunit)throwsInterruptedException;//等待时间也是相对时间但参数单位是纳秒返回值是nanosTimeout减去实际等待的时间longawaitNanos(longnanosTimeout)throwsInterruptedException;//等待时间是绝对时间如果由于等待超时返回返回值为false否则为truebooleanawaitUntil(Datedeadline)throwsInterruptedException;这些await方法都是响应中断的如果发生了中断会抛出InterruptedException但中断标志位会被清空。Condition还定义了一个不响应中断的等待方法voidawaitUninterruptibly();该方法不会由于中断结束但当它返回时如果等待过程中发生了中断中断标志位会被设置。一般而言与Object的wait方法一样调用await方法前需要先获取锁如果没有锁会抛出异常IllegalMonitorStateException。await在进入等待队列后会释放锁释放CPU当其他线程将它唤醒后或等待超时后或发生中断异常后它都需要重新获取锁获取锁后才会从await方法中退出。另外与Object的wait方法一样await返回后不代表其等待的条件就一定满足了通常要将await的调用放到一个循环内只有条件满足后才退出。一般而言signal/signalAll与notify/notifyAll一样调用它们需要先获取锁如果没有锁会抛出异常IllegalMonitorStateException。signal与notify一样挑选一个线程进行唤醒signalAll与notifyAll一样唤醒所有等待的线程但这些线程被唤醒后都需要重新竞争锁获取锁后才会从await调用中返回。ReentrantLock实现了newCondition方法通过它我们来看下条件的基本用法。我们实现与15.3节类似的例子WaitThread一个线程启动后在执行一项操作前等待主线程给它指令收到指令后才执行示例代码如代码所示。publicclassWaitThreadextendsThread{privatevolatilebooleanfirefalse;privateLocklocknewReentrantLock();privateConditionconditionlock.newCondition();Overridepublicvoidrun(){try{lock.lock();try{while(!fire){condition.await();}}finally{lock.unlock();}System.out.println(fired);}catch(InterruptedExceptione){Thread.interrupted();}}publicvoidfire(){lock.lock();try{this.firetrue;condition.signal();}finally{lock.unlock();}}publicstaticvoidmain(String[]args)throwsInterruptedException{WaitThreadwaitThreadnewWaitThread();waitThread.start();Thread.sleep(1000);System.out.println(fire);waitThread.fire();}}需要特别注意的是不要将signal/signalAll与notify/notifyAll混淆notify/notifyAll是Object中定义的方法Condition对象也有稍不注意就会误用。比如对上面例子中的fire方法可能会写为publicvoidfire(){lock.lock();try{this.firetrue;condition.notify();}finally{lock.unlock();}}写成这样编译器不会报错但运行时会抛出IllegalMonitorStateException因为notify的调用不在synchronized语句内。同样避免将锁与synchronized混用那样非常令人混淆比如publicvoidfire(){synchronized(lock){this.firetrue;condition.signal();}}记住显式条件与显式锁配合wait/notify与synchronized配合。2、生产者/消费者模式用wait/notify实现了生产者/消费者模式我们提到了wait/notify的一个局限它只能有一个条件等待队列分析等待条件也很复杂。在生产者/消费者模式中其实有两个条件一个与队列满有关一个与队列空有关。使用显式锁可以创建多个条件等待队列。下面我们用显式锁/条件重新实现下其中的阻塞队列如代码所示。staticclassMyBlockingQueueE{privateQueueEqueuenull;privateintlimit;privateLocklocknewReentrantLock();privateConditionnotFulllock.newCondition();privateConditionnotEmptylock.newCondition();publicMyBlockingQueue(intlimit){this.limitlimit;queuenewArrayDeque(limit);}publicvoidput(Ee)throwsInterruptedException{lock.lockInterruptibly();try{while(queue.size()limit){notFull.await();}queue.add(e);notEmpty.signal();}finally{lock.unlock();}}publicEtake()throwsInterruptedException{lock.lockInterruptibly();try{while(queue.isEmpty()){notEmpty.await();}Eequeue.poll();notFull.signal();returne;}finally{lock.unlock();}}}上述代码定义了两个等待条件不满notFull​、不空notEmpty​。在put方法中如果队列满则在notFull上等待在take方法中如果队列空则在notEmpty上等待。put操作后通知notEmpty, take操作后通知notFull。这样代码更为清晰易读同时避免了不必要的唤醒和检查提高了效率。Java并发包中的类ArrayBlockingQueue就采用了类似的方式实现。3、实现原理理解了显式条件的概念和用法我们来看下ReentrantLock是如何实现它的其new-Condition()的代码为publicConditionnewCondition(){returnsync.newCondition();}sync是ReentrantLock的内部类对象其newCondition()代码为finalConditionObjectnewCondition(){returnnewConditionObject();}ConditionObject是AQS中定义的一个内部类它的实现也比较复杂我们通过一些主要代码来简要探讨其实现原理。ConditionObject内部也有一个队列表示条件等待队列其成员声明为//条件队列的头节点privatetransientNodefirstWaiter;//条件队列的尾节点privatetransientNodelastWaiter;ConditionObject是AQS的成员内部类它可以直接访问AQS中的数据比如AQS中定义的锁等待队列。我们看下主要方法的实现。先看await方法如代码所示。我们通过添加注释解释其基本思路。publicfinalvoidawait()throwsInterruptedException{//如果等待前中断标志位已被设置直接抛出异常if(Thread.interrupted())thrownewInterruptedException();//1为当前线程创建节点加入条件等待队列NodenodeaddConditionWaiter();//2释放持有的锁intsavedStatefullyRelease(node);intinterruptMode0;//3放弃CPU进行等待直到被中断或isOnSyncQueue变为true//isOnSyncQueue为true表示节点被其他线程从条件等待队列//移到了外部的锁等待队列等待的条件已满足while(!isOnSyncQueue(node)){LockSupport.park(this);if((interruptModecheckInterruptWhileWaiting(node))!0)break;}//4重新获取锁if(acquireQueued(node,savedState)interruptMode!THROW_IE)interruptModeREINTERRUPT;if(node.nextWaiter!null)// clean up if cancelledunlinkCancelledWaiters();//5处理中断抛出异常或设置中断标志位if(interruptMode!0)reportInterruptAfterWait(interruptMode);}awaitNanos与await的实现是基本类似的区别主要是会限定等待的时间具体就不列举了。signal方法代码为publicfinalvoidsignal(){//验证当前线程持有锁if(!isHeldExclusively())thrownewIllegalMonitorStateException();//调用doSignal唤醒等待队列中第一个线程NodefirstfirstWaiter;if(first!null)doSignal(first);}doSignal的代码就不列举了其基本逻辑是将节点从条件等待队列移到锁等待队列调用LockSupport.unpark将线程唤醒。