通过保证在临界区上多个线程的相互排斥,线程间可以完全避免竞争状态的发生,但是有时候还是需要线程之间的相互协作。使用条件(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()<amount){
					System.out.println("\n"+_account.getAccountInfor()+" 无法去除¥"+amount +",余额不足");  

					newDeposit.await();
				}
			   _moneyCount-=amount;
			    Thread.sleep(1000);  
			   System.out.println(" \n取出了¥"+amount+" ,"+ _account.getAccountInfor());  
			} catch(InterruptedException ex){
				ex.printStackTrace();
			} finally {
				_lock.unlock();
			}

	   }
       public String getAccountInfor(){  
           return "当前账户的余额为:¥"+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截屏还真是不错,配合使用

,越来越上手了。