通过保证在临界区上多个线程的相互排斥,线程间可以完全避免竞争状态的发生,但是有时候还是需要线程之间的相互协作。使用条件(Condition)便于线程间通信。一个线程可以指定在某种条件下该做什么。标间是通过调用Lock对象的newCoditionn()方法来实现线程之间的相互通信的。
一旦创建一个条件,就可使用await()、signal()、signalAll()方法来实现线程间通信。await()方法可以让当前线程都处于等待状态,知道条件放生。signal()方法唤醒一个等待的线程,而signalAll()方法唤醒所有等待线程。
假设创建并启动两个任务,一个用来向账户存款,另一个从同一个账户取款。当取款数额大于账户余额的时,取款线程必须等待。不管什么时候,只要向账户新存了一笔资金,存款线程必须通知提款线程重新尝试。如果余额仍为达到取款数额、提款线程必须继续等待新的存款。
为了同步这些操作,使用一个由条件的锁newDeposit(即增加到账户的新存款)。如果余额小于取款数额,提款任务将等待newDeposit条件。当存款任务给账户增加资金时,存款任务唤醒等待中的提款任务在次尝试。
看下面的例子
package LianXi;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.newCachedThreadPool(); executor.execute(new WithdraoTask()); executor.execute(new AddMoneyTask()); } //执行任务想账户中加钱 public static class AddMoneyTask implements Runnable{ @Override public void run(){ while(true){ _account.deposit((int)(Math.random()*1000)); } } } //取款任务 public static class WithdraoTask implements Runnable{ @Override public void run(){ while(true){ _account.withDraw((int)(Math.random()*5000)); } } } //账户 public static class Account{ private static Lock _lock = new ReentrantLock(true); //创建一个锁 private static Condition newDeposit = _lock.newCondition(); // 创建一个条件 private int _moneyCount=0; public int getMoneyCount(){ return _moneyCount; } public void deposit(int amount){ _lock.lock(); //获得锁 int newMoneyCount = getMoneyCount()+amount; try{ _moneyCount=newMoneyCount; System.out.println("\n新存入了$"+amount+_account.getAccountInfor()); newDeposit.signalAll(); Thread.sleep(1000); } catch(InterruptedException ex) { } finally { _lock.unlock(); } } public void withDraw(int amount){ _lock.lock(); try{ while(getMoneyCount()运行结果:
-
需要注意的地方:1、在取款任务中使用Wile判断,避免永久等待。因为当存款任务调用signalAll()的时候while条件依然为true 。如要再次判断余额和要取的数额,以确定是否可以取款。
2、一旦线程调用雕件的await()方法,进会进入等待状态,如果忘记调用signal 或者signalAll() 就回进入永久等待状态。
3、条件由Lock对象创建,为了嗲用任务方法(如:await。signal 、signalAll),必须首先获得锁。,如果没有获得锁,就调用这些方法会排除 IIlegalMonitorStateException异常。
锁和条件java5 引入的内容,在java 5 之前,线程通信是使用对象的内容见识起编程实现的。锁和条件与内置监视器相比是非常强大而且灵活的。
监视器(monitor)是一个相互作用切具备同步能力的对象,监视器中的一个时间点上,只能有一个线程执行一个方法。线程通过获取监视器上的锁进入监视器,并且通过在方法或块块上使用synchronized关键字来实现。在执行同步方法或块之前,线程必须获取锁。如果条件不适合线程在监视器内继续执行,线程可能在监视器中等待。可以对监视器对象调用wait()方法来释放锁,这样其它的一些监视器中的线程就可以获取它,也就有可能改变监视器的状态。当条件合适时,另一个线程可以调用notiry()或者notifyAll()房东发来说通知一个或多有的等待线程重新获取锁,并且恢复执行。
wait()、notify()、notifyAll*()方法必须在这些方法的接收对象的同步方法或同步块中调用,否则,就回出现IIlegalMonitorStateException异常。
可以将上面的程序修改成为moniitor版本。
当调用wait()方法时,它中止线程的同事释放对象的锁,当线程被通知之后重新启动时,锁就被重新自动获取。对象上的wait()、nitify()、notifyAll方法类似于状态撒谎能够的 await()、signal()和aignalAll()方法。
题外话。PowerDesigner 、画图工具、qq截屏还真是不错,配合使用 ,越来越上手了。