引言
Object.wait和notify方法利用在对象上的锁实现了等待通知机制,它们必须与synchronized关键字配合使用。Condition接口提供了与wait/notify类似的等待通知机制,它需要配合Lock来使用。除了提供类似的功能,在某些方面上,Condition与wait/notify还有些不同。这篇文章,我们来看看Condition的用法。
方法分类
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();
其中,await**方法用来将当前线程挂起处于等待状态,可以从几个维度对await类的方法进行分类:
按照是否能响应中断awaitUninterruptibly()方法自己一类,其他几个方法一类,可以看到其他几个方法都抛出了InterruptedException,意味着它们会以抛出异常的形式响应线程中断。
按照线程挂起是否有时间限制,分为两类,await()和awaitUninterruptibly()没有时间限制,如果没有其他导致线程唤醒(例如其他线程执行signal或者signalAll方法或者线程中断)会一直处于等待状态,而awaitNanos、await和awaitUntil这三个方法的挂起时间都有时间限制。
signal和signalAll两个方法就可以对比Object的notify和notifyAll方法来想象,就是用来唤醒一个或者所有被挂起的线程。
看完了大致的分类,我们来详细看一下这些方法的使用和相关特性。
方法的使用与注意事项
await和sign类方法前提必须使用lock获取锁
看下面的例子:
public class ConditionTest {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}}
执行会抛出异常:
Exception in thread "main" java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)at person.andy.concurrency.lock.condition.ConditionTest.main(ConditionTest.java:12)
这个异常我们在wait/notify里面也遇到过,意思是没有获取锁。在使用Condition的await类和sign类方法时,都必须先用lock提前获取锁。我改一下这个例子:
public class ConditionTest {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();lock.lock();try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}lock.unlock();}}
调用await()方法后会立即释放锁
public class ConditionTest {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();try {System.out.println("await线程获得了锁");condition.await();Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}lock.unlock();}},"await_thread");thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();System.out.println("main线程获得了锁");lock.unlock();}}
在这个例子中,thread线程执行await方法之后,main方法马上就会获取到锁,然后输出“main线程获得了锁”,这个与Object.wait方法是一样的逻辑,都是执行完成后线程立刻处于等待状态,同时锁被释放。
调用sign方法后需要等到lock.unlock才会释放锁
public class ConditionTest {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();try {System.out.println("await线程获得了锁");condition.await();System.out.println("await开始睡眠");Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}lock.unlock();}},"await_thread");thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();System.out.println("main线程获得了锁");condition.signalAll();Thread.sleep(10000);lock.unlock();}}
这个例子中,main线程调用了signalAll方法后睡眠10秒钟,在这个期间,锁不会被释放,thread线程也不会被唤醒,而是等main线程执行完lock.unlock方法之后,thread线程才会被唤醒。
使用await和sign实现生产者消费者模型
下面是一个完整的用await和sign实现生产者消费者模型的实例:
public class AwaitSign {private static int count = 0;private static Lock lock = new ReentrantLock();private static Condition condition = lock.newCondition();public static void main(String[] args) {ExecutorService producers = new ThreadPoolExecutor(5,5,1, TimeUnit.SECONDS,new LinkedBlockingDeque<>());ExecutorService consumers = new ThreadPoolExecutor(3,3,1,TimeUnit.SECONDS,new LinkedBlockingDeque<>());for(int i=0;i<1000;i++){producers.submit(new AwaitSign.Producer());consumers.submit(new AwaitSign.Consumer());}}static class Consumer implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();while(count == 0){try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}count --;System.out.println("消费者"+Thread.currentThread()+"消费之后count="+count);condition.signalAll();lock.unlock();}}static class Producer implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}lock.lock();while(count == 100){try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}count ++;System.out.println("生产者"+Thread.currentThread()+"生产之后count="+count);condition.signalAll();lock.unlock();}}}
小结
Condition与Lock结合使用的方式类比wait/notify与synchronized关键字的使用,它们都能实现等待通知机制。
