天天看點

多線程經典案例:模仿儲戶取錢

銀行取錢肯定不能透支這個是衆所周知的,實作此功能就需要借助多線程的知識!

先來看一下采取同步措施前産生的結果:

多線程經典案例:模仿儲戶取錢

好的,我們先來定義一個賬戶類:

/**
 * 賬戶類,含卡号,還有餘額 通過同步方法防止出現餘額為負的情況
 * 
 * @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();
  }
}      

繼續閱讀