1. 程式人生 > >執行緒同步:銀行帳戶存、取款問題

執行緒同步:銀行帳戶存、取款問題

1.銀行賬戶類

package com.bankAccount.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
	//顯式定義Lock物件
	private final Lock lock=new ReentrantLock(); 
	//獲得指定Lock物件對應的Condition
	private final Condition cond=lock.newCondition();
	//封裝賬戶編號,賬戶餘額的兩個成員變數
	private String accoutNo;
	private double balance;
	//標識賬戶中是否已有存款的旗標
	private boolean flag=false;
	public Account(){}
	//構造器
	public Account(String accountNo,double balance)
	{
		this.accoutNo=accountNo;
		this.balance=balance;
	}
	public String getAccoutNo() {
		return accoutNo;
	}
	public void setAccoutNo(String accoutNo) {
		this.accoutNo = accoutNo;
	}
	//因為賬戶餘額不可以隨便修改,所以只為balance提供getter方法
	public double getBalance() {
		return this.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);
				//將標識賬戶是否已有存款的旗標設為false
				flag=false;
				//喚醒其他程序
				cond.signalAll();
			}
		}
		catch(InterruptedException ex)
		{
			ex.printStackTrace();
		}
		finally
		{
			lock.unlock();
		}		
	}
	public void deposit(double depositAmount)
	{
		lock.lock();
		try{
			//如果flag為真,表明賬戶中已經有人存錢進去,存錢方法阻塞
			if(flag)
			{
				cond.await();
			}
			else
			{
				//執行存款操作
				System.out.println(Thread.currentThread().getName()+
						"存款:"+depositAmount);
				balance+=depositAmount;
				System.out.println("賬戶餘額為:"+balance);
				//將表示是否已有存款的旗標設為true
				flag=true;
				//喚醒其他執行緒
				cond.signalAll();
			}
		}
		catch(InterruptedException ex)
		{
			ex.printStackTrace();
		}
		finally{
			lock.unlock();
		}
	}
	public int hashCode()
	{
		return accoutNo.hashCode();
	}
	public boolean equals(Object obj)
	{
		if(this==obj)
			return true;
		if(obj!=null&&obj.getClass()==Account.class)
		{
			Account target=(Account)obj;
			return target.getAccoutNo().equals(accoutNo);
		}
		return false;
	}
}

2.存款執行緒
package com.bankAccount.test;
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++)
		{
			account.deposit(depositAmount);
		}
	}
}

3.取款執行緒
package com.bankAccount.test;
public class DrawThread extends Thread{
	//模擬使用者賬戶
	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()
	{
		//使用account作為同步監視器,任何執行緒進入下面同步程式碼塊之前
		//必須先獲得對account賬戶的鎖定---其他執行緒無法獲得鎖,也就無法修改它
		//該做法符合:"加鎖->修改->釋放鎖"
		for(int i=0;i<100;i++)
		{
			account.draw(drawAmount);
		}
	}
}
4.測試類
package com.bankAccount.test;
public class DrawTest {
	public static void main(String[] args) {
		//建立一個賬戶
		Account acct=new Account("123",0);
		//模擬兩個執行緒對同一個賬戶取 錢
		new DrawThread("取錢者",acct,800).start();
		new DepositThread("存款者甲",acct,800).start();
		new DepositThread("存款者乙",acct,800).start();
		new DepositThread("存款者丙",acct,800).start();
	}
}

結果: