1. 程式人生 > >執行緒間協作

執行緒間協作

要點提示:鎖上的條件可以用於協調執行緒之間的互動

  通過保證臨界區上多個執行緒的相互排斥,執行緒同步完全可以避免競爭條件的發生,但是有時候還需要執行緒之間進行協作。可以使用條件實現執行緒間通訊。一個執行緒可以指定某種條件下該做什麼,條件是通過lock物件的newCondition()方法而建立的物件,一旦建立了條件,就可以使用await()signal()signAll()方法來實現執行緒之間的相互通訊。

《interface》                           java.util.concurrent.Condiction.
+await():void                                         引起當前執行緒等待

+signal():void                                        喚醒一個等待執行緒

+signalAll():Condition                            喚醒所有等待執行緒

  還是通過一個例子來演示一下執行緒通訊吧!假設建立並啟動兩個任務,一個用來向賬戶中存款,另一個從賬戶中取款。但提款的數額大於當前餘額時,提款執行緒必須等待。不管什麼時候只要賬戶新存入一筆資金儲存執行緒必須通知提款執行緒重新嘗試。如果餘額仍然不足提款執行緒繼續等待新的存款。

  為了同步這些操作,使用一份具有條件的鎖newDeposit(即增加到賬戶的新存款)。如果餘額小於取款數額,提款任務將等待newDeposit條件。當存款任務給賬戶增加資金時,存款任務喚醒等待中的提款任務再次嘗試。兩個任務的互動如圖:

注:條件newDeposit用於兩個執行緒間通訊

  從lock物件中建立條件。為了使用條件,必須先獲取鎖。await();方法讓執行緒等待並且自動釋放條件上的鎖。一旦條件正確執行緒重新獲取鎖並繼續執行。

假設初試餘額為0,存入額和提取額為隨機產生。程式清單及執行結果如下:

package threadCooperation.cn;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import threadCooperation.cn.ThreadCooperation.DepositTask;
import threadCooperation.cn.ThreadCooperation.Withdrawtask;
/**
 * 
 * @author victoy
 *用於測試執行緒間通訊的一個demo
 */
public class ThreadCooperation {
	private static Account account=new Account();
	public static void main(String[] args) {
		//create a thread pool with two threads
		ExecutorService executor =Executors.newFixedThreadPool(2);
		executor.execute(new DepositTask());
		executor.execute(new Withdrawtask());
		executor.shutdown();
		System.out.println("Thread1\t\tThread2\t\tBalance");
		
	}
/**
 * 
 * @author victoy
 *kepp adding an amount to the account
 */
	public static class Withdrawtask implements Runnable {

		@Override
		public void run() {
			while(true){
				account.withdraw((int)(Math.random()*10)+1);
			}
		}

	}
/**
 * 
 * @author victoy
 *keep subtracting an amount from the account
 */
	public static class DepositTask implements Runnable {

		@Override
		public void run() {
			try {
			while (true) {
				account.deposit((int)(Math.random()*10)+1);
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
				
			}
		}

	}
	
	private  static class Account{
		//cerate a lock
		private static Lock lock=new ReentrantLock();
		//create a condintion
		private static Condition newDeposit=lock.newCondition();
		private int balace=0;
		public int getBalace(){
			return balace;
			
		}
		public void withdraw(int amount){
			lock.lock();
			try {
			while(balace<amount){
				System.out.println("\t\t\tWait for a deposit");
					newDeposit.await();
					// TODO Auto-generated catch block
				}
			balace-=amount;
			System.out.println("\t\t\tWithdraw"+amount+"\t\t"+getBalace());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			finally {
				lock.unlock();
			}
				
		}
		public void deposit(int amount){
			lock.lock();
			try{
				
				balace+=amount;
				System.out.println("Deposit"+amount+"\t\t\t\t\t"+getBalace());
				newDeposit.signalAll();
			}finally{
				lock.unlock();
			}
		
			
		}
		
	}


}

執行結果:

說明:這個例項建立了一個名為Account的內部類來模擬賬戶,該類中有兩個方法deposit()和withdraw()。建立一個名為depositTask的類向賬戶新增金額,withdrawTask類向賬戶餘額提取金額,再建立一個主類用於建立並啟動兩個執行緒。