關于線程安全問題,有一個經典的問題:銀行取錢的問題。銀行取錢的基本流程可以分為如下幾個步驟:
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
竟然餘額為負數,這個肯定是銀行不能接受的,這就是多線程高并發情況下引發的線程安全問題。