通过保证在临界区上多个线程的相互排斥,线程同步完全可以避免竞争状态的发生,但有时还需要线程之间的协作。
使用Condition便于线程间通信,它是通过调用Lock对象的newCondition()方法而创建的对象。
创建Condition后,就可以使用await() 、signal()、 signalAll()方法来实现线程间的相互通信。
java.util.concurrent.Condition
+await():void 让当前线程处于等待状态,直到条件发生
+signal():void 唤醒一个等待的线程
+signalAll():Condition 唤醒所有等待的线程
为使用Condition,必须首先获取锁。
线程间通信的流程就可以总结为:一个线程要访问某需要同步处,申请获取锁,访问时发现不满足自己可以执行的条件,进入等待状态并释放锁;
而唤醒程序的流程一般是首先去获取锁,访问后条件发生改变,唤醒 await等待中的线程,等待中的线程由此重新获取锁,最后释放锁。
例子,存取款任务线程间的通信:
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class ThreadCooperation {
private static Account account=new Account();
public static void main(String[] args){
ExecutorService executor=Executors.newFixedThreadPool(2);
executor.execute(new DepositTask());
executor.execute(new WithdrawTask());
executor.shutdown();
System.out.println("Thread 1\t\tThread 2\t\tBalance");
}
public static class DepositTask implements Runnable{
public void run(){
try{
while(true){
account.deposit((int)(Math.random()*10)+1);
Thread.sleep(1000);
}
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
}
public static class WithdrawTask implements Runnable{
public void run(){
while(true){
account.withdraw((int)(Math.random()*10)+1);
}
}
}
private static class Account{
//创建锁
private static Lock lock=new ReentrantLock();
//创建条件
private static Condition newDeposit=lock.newCondition();
private int balance=0;
public int getBalance(){
return balance;
}
//取款
public void withdraw(int amount){
//获取锁
lock.lock();
try{
while(balance<amount){
System.out.println("\t\tWait for a deposit");
//线程进入等待状态,暂时释放锁直到被唤醒
newDeposit.await();
}
balance-=amount;
System.out.println("\t\tWithdraw "+amount+"\t\t"+getBalance());
}
catch(InterruptedException ex){
ex.printStackTrace();
}
finally{
//释放锁
lock.unlock();
}
}
//存款
public void deposit(int amount){
//获取锁
lock.lock();
try{
balance+=amount;
System.out.println("Deposit "+amount+"\t\t\t\t\t"+getBalance());
//条件发生变化,唤醒等待的线程们
newDeposit.signalAll();
}
finally{
//释放锁
lock.unlock();
}
}
}
}
await()方法让线程等待并且自动释放Condition上的锁。一旦条件正确,线程重新获取锁并继续执行。
注意:
线程调用条件上的await()就进入等待状态,等待恢复信号。若忘记对条件使用signal()或signalAll(),那么线程就会一直等待下去。
条件由Lock对象创建。为了调用任意方法(如await、signal、signalAll),必须先拥有锁。若没有获取锁就调用这些方法,会抛出IllegalMonitorStateException异常。
Java 5之前使用的内置监视器(monitor)中对象上的wait()、notify()和notifyAll()方法类似于状态上的await()、signal()和signalAll()方法。