Java多線程協調控制之wait&notifyAll

最近在複習Java基礎,看到多線程這塊順便寫寫多線程的協調控制程序。


需求:假設系統中有兩個線程分別代表取款者和存款者,現在系統的要求是存款者和取款者不斷的重複存、取款操作,

並且要求每當有存款者將錢存入指定賬戶中時,取款者就立即取出這筆錢,即不允許存款者連續兩次存錢,也不允許

取款者兩次取錢。


下面代碼實現:

1.首先是賬戶Account類;

package com.xjtu.cruise.soft.thread;

public class Account {

	/**
	 * @author Cruise
	 * @param args
	 */
	private String accountNo;
	//標識賬戶是否還有存款的
	private boolean flag = false;
	private double balance;

	public Account(){}
	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	public double getBalance(){
		return balance;
	}
	public synchronized void draw(double drawAmount) {
		try {
			if (!flag) {  //如果賬戶沒錢
				wait();  //阻塞當前的方法,並釋放同步鎖(這裏就是this,也就是調用此方法的對象)
			} else {
				System.out.println("線程:" + Thread.currentThread().getName()
						+ "取款" + drawAmount);
				balance -= drawAmount;
				System.out.println("賬戶餘額:" + balance);
				flag = false;
				notifyAll();  //喚醒等待此同步鎖的所有線程
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void deposit(double depositAmount) {

		try{
			if(flag){ //如果賬戶有存入的錢
				wait();
			}else{
				System.out.println("線程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
				balance += depositAmount;
				System.out.println("賬戶餘額:" + balance);
				flag = true;
				notifyAll();
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}

}



2.接下來是存取款線程類(DrawThread,DepositThread)

package com.xjtu.cruise.soft.thread;

public class DepositThread extends Thread {

	private Account account;
	private double depositAmount;

	public DepositThread(String name, Account account, double depositAmount) {
		super(name);
		this.account = account;
		this.depositAmount = depositAmount;
	}
	
	public void run(){
		for(int i=0; i<100; i++){
//			System.out.println("線程:" + Thread.currentThread().getName()+"執行存款操作。");
			account.deposit(depositAmount);
		}
	}
}

package com.xjtu.cruise.soft.thread;

public class DrawThread extends Thread {
/**
     * @author Cruise
     * @param args
     */
	private Account account;
	private double drawAmount;

	public DrawThread(String name, Account account, double drawAmount) {
		super(name);
		this.account = account;
		this.drawAmount = drawAmount;
	}
	
	public void run(){
		for(int i=0; i<100; i++){
//			System.out.println("線程:" + Thread.currentThread().getName()+"執行取錢操作。");
			account.draw(drawAmount);
		}
	}
}

3.奉上測試類TestDraw

package com.xjtu.cruise.soft.thread;

public class TestDraw {

	/**
	 * @author Cruise
	 * @param args
	 */
	public static void main(String[] args) {
		Account account = new Account("123", 0);
		new DrawThread("取錢者", account, 800).start();
		new DepositThread("存款者甲",account, 800).start();
		new DepositThread("存款者乙",account, 800).start();

	}

}

OK, 一個使用wait和notifyAll控制線程協調的Demo完成,運行結果如下:


下面再使用條件變量的方式來協調線程的同步,具體來說就是使用Lock對象來保證同步,而不是用Synchronized關鍵字。

通過Lock實例調用其newCondition()方法得到對應的Condition對象,接着使用此Condition實例的方法來保證線程的協

調。這裏用到了Condition的await和signalAll方法。

將上面的Account類修改如下,其他的線程類不變。

package com.xjtu.cruise.soft.thread;

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

public class Account {

	/**
	 * @author Cruise
	 * @param args
	 */
	private final Lock lock = new ReentrantLock();//定義Lock對象
	private final Condition cond = lock.newCondition();//獲取Lock對象對應的條件變量
	private String accountNo;
	//標識賬戶是否還有存款的
	private boolean flag = false;
	private double balance;

	public Account(){}
	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	public double getBalance(){
		return balance;
	}
	public void draw(double drawAmount) {
		lock.lock();//加鎖
		try {
			if (!flag) {  //如果賬戶沒錢
				cond.await();  
			} else {
				System.out.println("線程:" + Thread.currentThread().getName()
						+ "取款" + drawAmount);
				balance -= drawAmount;
				System.out.println("賬戶餘額:" + balance);
				flag = false;
				cond.signalAll();  //喚醒該Lock對象對應的其他線程
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			lock.unlock();//使用finally塊確保釋放鎖
		}
	}

	public void deposit(double depositAmount) {
		lock.lock();//加鎖
		try{
			if(flag){ //如果賬戶有存入的錢
				cond.await();
			}else{
				System.out.println("線程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
				balance += depositAmount;
				System.out.println("賬戶餘額:" + balance);
				flag = true;
				cond.signalAll();//喚醒該Lock對象對應的其他線程
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{//使用finally塊確保釋放鎖
			lock.unlock();
		}
	}

}

運行上面的測試類,可以得到如下的運行結果:

從上面的兩個運行結果可以發現,雖然兩種方法都可以保證線程的同步和協調運行,但是線程的調度機制還是有些許的

差別,這點可以從上圖的紅框看出。當然底層的具體實現,因爲不大瞭解,這裏就不作具體的闡述了(廢話,不瞭解當

然闡述不了,^_^),希望瞭解的同學能幫忙解答下,very thx!


發佈了64 篇原創文章 · 獲贊 28 · 訪問量 49萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章