天天看點

多線程(四)線程的同步之線程安全問題

關于線程安全問題,有一個經典的問題:銀行取錢的問題。銀行取錢的基本流程可以分為如下幾個步驟:

1、使用者輸入賬号、密碼,系統判斷使用者的賬戶、密碼是否比對;

2、使用者輸入取款金額;

3、系統判斷賬戶餘額是否大于取款金額;

4、如果餘額大于取款金額,取款成功;如果餘額小于取款金額,則取款失敗。

初步看上去,這個流程确實就我們日常生活中經常見到的,沒有任何問題。但是如果将這個流程放到多線程并發的場景下,就有可能出現問題。注意,是有可能,并不是一定。

下面,我們按照上面的流程去編寫取款程式,而且我們隻是使用2條線程來模拟取錢操作,模拟兩人使用同一賬戶并發取錢問題。當然,我們不管檢查賬戶和密碼的操作,僅僅模拟後面3步操作。

package gblw.first;

public class Account {
	//封裝賬号編碼、賬号餘額兩個屬性
	private String accountNo;
	private double balance;
	
	public Account(String accountNo,double balance){
		this.accountNo=accountNo;
		this.balance=balance;
	}

	public String getAccountNo() {
		return accountNo;
	}

	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}
	
	//下面兩個方法根據accountNo來計算Account的hashCode和判斷equals
	public int hashCode(){
		return accountNo.hashCode();
	}
	
	public boolean equals(Object obj){
		if(obj!=null&&obj.getClass()==Account.class){
			Account target=(Account) obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}
           
package gblw.first;

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(){
		//賬戶餘額大于取錢數目
		if(account.getBalance()>=drawAmount){
			//吐出鈔票
			System.out.println(getName()+"取錢成功!吐出鈔票:"+drawAmount);
			try {
				Thread.sleep(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			//修改餘額
			account.setBalance(account.getBalance()-drawAmount);
			System.out.println("\t餘額為:"+account.getBalance());
		}else{
			System.out.println(getName()+"取錢失敗!餘額不足!");
		}
	}	
}
           
package gblw.first;

public class TestDraw {
	public static void main(String[] args) throws InterruptedException {
		//建立一個賬戶
		Account account=new Account("1234567", 1000);
		//模拟兩個線程對同一個賬戶取錢
		new DrawThread("甲", account, 800).start();
		new DrawThread("乙", account, 800).start();
	}
}
           

運作後的結果如下:

乙取錢成功!吐出鈔票:800.0

甲取錢成功!吐出鈔票:800.0

餘額為:200.0

餘額為:-600.0

竟然餘額為負數,這個肯定是銀行不能接受的,這就是多線程高并發情況下引發的線程安全問題。

多線程(四)線程的同步之線程安全問題