在之前的Java并发(一)wait()与notifyAll()一文中的例子中,我们使用了wait()和notifyAll()来模拟了给汽车打蜡和抛光的情景。在JavaSE5中,还提供了java.util.concurrent.locks.Condition对象供我们使用。你可以在Condition上调用await()来挂起一个任务。当外部条件发生变化,意味着某个任务应该继续执行时,你可以通过调用signal()来通知这个任务,或者调用signalAll()来唤醒所有在这个Condition上被其自身挂起的任务(与使用signal()相比,signalAll()是更安全的方式)。
下面是WaxOnMatic.java的重写版本,它包含了一个Condition,用来在waitForWaxing()或waitForBuffing()内部挂起一个任务:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Car {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean waxOn = false;//是否上蜡
//上蜡
public void waxed() {
lock.lock();
try {
waxOn = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
//抛光
public void buffed() {
lock.lock();
try {
waxOn = false;
condition.signalAll();
} finally {
lock.unlock();
}
}
//等待上蜡
public void waitForWaxing() throws InterruptedException {
lock.lock();
try {
while(waxOn == false) {
condition.await();
}
} finally {
lock.unlock();
}
}
//等待抛光
public void waitForBuffing() throws InterruptedException {
lock.lock();
try {
while(waxOn == true) {
condition.await();
}
} finally {
lock.unlock();
}
}
}
class WaxOnTask implements Runnable {
private Car car;
private String name;
public WaxOnTask(String name, Car car) {
this.name = name;
this.car = car;
}
@Override
public void run() {
try {
while(!Thread.interrupted()) {
System.out.println("[" + name + "] is Wax on!");//正在上蜡
TimeUnit.MILLISECONDS.sleep(300);
car.waxed();//上蜡完成
car.waitForBuffing();//等待抛光
}
} catch (InterruptedException e) {
System.out.println("[" + name + "] Exiting WaxOnTask via interrupt.");
}
}
}
class BuffTask implements Runnable {
private Car car;
private String name;
public BuffTask(String name, Car car) {
this.name = name;
this.car = car;
}
@Override
public void run() {
try {
while(!Thread.interrupted()) {
car.waitForWaxing();//等待上蜡
System.out.println("[" + name + "] Buffing...");//正在抛光
TimeUnit.MILLISECONDS.sleep(300);
car.buffed();//抛光完成
}
} catch (InterruptedException e) {
System.out.println("[" + name + "] Exiting BuffTask via interrupt.");
}
}
}
public class WaxOMatic2 {
public static void main(String[] args) throws Exception {
Car car = new Car();
ExecutorService exec = Executors.newCachedThreadPool();
//上蜡
exec.execute(new WaxOnTask("Waxx", car));
//抛光
exec.execute(new BuffTask("Buff", car));
//运行一段时间,停止ExecutorService
TimeUnit.SECONDS.sleep(3);
exec.shutdownNow();
}
}
执行结果:
[Waxx] is Wax on!
[Buff] Buffing...
[Waxx] is Wax on!
[Buff] Buffing...
[Waxx] is Wax on!
[Buff] Buffing...
[Waxx] is Wax on!
[Buff] Buffing...
[Waxx] is Wax on!
[Buff] Buffing...
[Buff] Exiting BuffTask via interrupt.
[Waxx] Exiting WaxOnTask via interrupt.
从代码中可以看到,Car的构造器中,单个的Lock将产生一个Condition对象,这个对象被用来管理任务之间的通信。但是,这个Condition对象不包含任何有关处理状态的信息,因此你需要管理额外的表示处理状态的信息,即boolean waxOn。
注意:每个lock()的调用都必须紧跟一个try-finally子句,用来保证在所有情况下都可以释放锁。在使用内建版本时,任务在可以调用await(),signal()或signalAll()之前,必须拥有这个锁。
另外还需要注意的是,这个解决方案比之前一个更加复杂,在本例中这种复杂性并未使你收获更多。Lock和Condition对象只有在更加困难的多线程问题中才是必需的。