Condition是Java并发编程中用于线程间协作的一种机制,它提供了比传统的Object监视器(基于wait()/notify()方法)更灵活和高效的线程通信方式,以下是对Condition API的详细解析:
1、基本概念
定义:Condition是一个接口,位于java.util.concurrent.locks包中,它依赖于Lock接口,通常与ReentrantLock一起使用。
作用:Condition提供了一种在多线程环境下进行线程间协调通信的机制,使得某个或某些线程能够等待某个条件(Condition)成立,只有当该条件满足时(通过signal或signalAll方法被调用),这些等待的线程才会被唤醒,从而重新争夺锁。
2、常用方法
await():使当前线程进入等待状态,直到其他线程调用signal()或signalAll()方法唤醒它,或者当前线程被中断。
await(long time, TimeUnit unit):使当前线程在给定的时间内等待,如果在指定时间内没有被唤醒,则超时并退出等待状态。
awaitNanos(long nanosTimeout):与await(long time, TimeUnit unit)类似,但参数是以纳秒为单位的超时时间。
awaitUntil(Date deadline):使当前线程等待到指定的日期时间,如果在该时间之前没有被唤醒,则超时并退出等待状态。
awaitUninterruptibly():使当前线程进入等待状态,直到其他线程调用signal()或signalAll()方法唤醒它,该方法对中断不敏感。
signal():唤醒在此Condition上等待的一个线程,如果有多个线程在等待,那么选择唤醒哪个线程是由调度程序决定的。
signalAll():唤醒在此Condition上等待的所有线程。
3、使用示例
生产者消费者模式:Condition常用于实现生产者消费者模式,一个缓冲区和一个Condition变量notEmpty(表示缓冲区是否为空)以及另一个Condition变量notFull(表示缓冲区是否已满),生产者线程在缓冲区满时调用notFull.await()进入等待状态,消费者线程在缓冲区空时调用notEmpty.await()进入等待状态,当消费者消费了一个元素后,它会调用notEmpty.signal()唤醒一个生产者线程;当生产者生产了一个元素后,它会调用notFull.signal()唤醒一个消费者线程。
4、注意事项
锁的使用:Condition对象必须与Lock配合使用,并且在调用Condition的await()、signal()等方法时,必须在lock的保护之内,即必须在lock.lock()和lock.unlock()之间调用。
中断处理:当线程在await()方法中等待时,如果其他线程中断了当前线程,那么当前线程会抛出InterruptedException异常,此时应该正确处理中断异常,通常是恢复中断状态并退出等待。
以下是两个关于Condition API的常见问题及解答:
1、为什么需要Condition而不是直接使用Object的wait()和notify()方法?
答:虽然Object的wait()和notify()方法也能实现线程间的协作,但它们存在一些局限性,它们只能与synchronized关键字一起使用,而Condition更加灵活,可以与任何实现了Lock接口的锁一起使用,Condition提供了更多的等待方式和更细粒度的控制,可以更好地满足复杂的线程间协作需求。
2、Condition的await()方法是否会释放锁?
答:是的,当线程调用Condition的await()方法时,它会释放与之关联的锁,并且线程会进入等待状态,当其他线程调用signal()或signalAll()方法唤醒等待的线程时,被唤醒的线程需要重新获取锁才能继续执行,这是Condition与Object的wait()方法的一个重要区别,因为Object的wait()方法不会释放锁。