多執行緒經典案例:模仿儲戶取錢
阿新 • • 發佈:2019-01-29
銀行取錢肯定不能透支這個是眾所周知的,實現此功能就需要藉助多執行緒的知識!
先來看一下采取同步措施前產生的結果:
好的,我們先來定義一個賬戶類:
/** * 賬戶類,含卡號,還有餘額 通過同步方法防止出現餘額為負的情況 * * @author Mr.Gao */ public class Account { private String accountNo; private double balance; public Account(String accountNo, double balance) { super(); 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; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((accountNo == null) ? 0 : accountNo.hashCode()); long temp; temp = Double.doubleToLongBits(balance); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Account other = (Account) obj; if (accountNo == null) { if (other.accountNo != null) return false; } else if (!accountNo.equals(other.accountNo)) return false; if (Double.doubleToLongBits(balance) != Double .doubleToLongBits(other.balance)) return false; return true; } public String toString() { return "Account [accountNo=" + accountNo + ", balance=" + balance + "]"; } /** * 同步取錢方法,防止餘額為負 * @param accountAmount */ public synchronized void draw(double accountAmount) { if (balance > accountAmount) { System.out.println(Thread.currentThread().getName() + " 取錢 " + accountAmount + " 成功"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } balance -= accountAmount; System.out.println("餘額為\t" + balance); } else { System.err.println("取錢失敗"); } } }
定義一個執行緒類,主要演示同步方法,註釋的部分為同步程式碼塊的內容:
/** * 取錢執行緒採用的是繼承的方式 * 無須自己實現取錢的操作,直接呼叫account的draw()方法實現,符合"加鎖--修改--釋放鎖"的邏輯 * 符合 領域驅動設計(每個類都是完整的領域物件,例如使用者賬戶提供相應的方法去處理賬戶所對應的需求) * @author Mr.Gao * */ 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() { // synchronized (account) { // if (account.getBalance() > drawAmount) { // System.out.println(this.getName() + " 取錢 " + drawAmount // + " 成功"); // // try { // Thread.sleep(100); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // account.setBalance(account.getBalance() - drawAmount); // System.out.println("餘額為\t" + account.getBalance()); // // } else { // System.out.println("餘額不足"); // } // } /* * 直接呼叫account物件的draw()方法,同步方法的同步監視器是this, * this代表呼叫draw()方法的物件,也就是說,再進入draw()方法前先對account物件進行加鎖 */ account.draw(drawAmount); } }
測試程式碼:
/**
* 結果測試
*
* @author Mr.Gao
*
*/
public class AccountTest {
public static void main(String[] args) {
Account acc = new Account("123", 1000);
new DrawThread("甲", acc, 800).start();
new DrawThread("乙", acc, 800).start();
}
}
輸出效果: