关于线程安全问题有一个经典的例子就是----银行取钱问题
- 用户输入账号密码,系统匹配账号密码是否正确
- 用户输入取款金额
- 系统判断账号余额是否大于取款金额
- 如果余额大于取款金额则取款成功,反正取款失败
package com.synchronized1; public class Account { //账号ID 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; } @Override 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; } @Override 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; } }
package com.synchronized1; public class DrawMoney implements Runnable { private Account account; private double drawAmount; private String name; public DrawMoney(String name,Account account,double drawAmount) { this.name = name; this.account = account; this.drawAmount = drawAmount; } @Override public void run() { if(account.getBalance()>drawAmount){ System.out.println(name+"取钱成功,吐出钞票:"+drawAmount); account.setBalance(account.getBalance()-drawAmount); System.out.println("余额:"+account.getBalance()); }else{ System.out.println("取钱失败,余额不足!"); } } }
package com.synchronized1; public class DrawMain { public static void main(String[] args) { Account ac=new Account("111", 1000); new Thread(new DrawMoney("甲", ac, 800)).start(); new Thread(new DrawMoney("乙", ac, 800)).start(); } }
乙取钱成功,吐出钞票:800.0 甲取钱成功,吐出钞票:800.0 余额:200.0 余额:-600.0
问题出现了:账户余额只有1000的时候取出了1600
采用synchronized保证线程安全:
package com.synchronized2; public class DrawMoney implements Runnable { private Account account; private double drawAmount; private String name; public DrawMoney(String name,Account account,double drawAmount) { this.name = name; this.account = account; this.drawAmount = drawAmount; } @Override public void run() { //使用account作为同步监视器,任何线程进入下面的同步代码块之前必须先 //获得account账户的锁定 synchronized(account){ if(account.getBalance()>drawAmount){ System.out.println(name+"取钱成功,吐出钞票:"+drawAmount); account.setBalance(account.getBalance()-drawAmount); System.out.println("余额:"+account.getBalance()); }else{ System.out.println("取钱失败,余额不足!"); } } } }
甲取钱成功,吐出钞票:800.0 余额:200.0 取钱失败,余额不足!
更好的设计方式:
package com.synchronized3; public class Account{ //账号ID private String accountNo; //余额 private double balance; public Account(String accountNo, double balance) { super(); this.accountNo = accountNo; this.balance = balance; } public synchronized void draw(double drawAmount){ if(balance>=drawAmount){ System.out.println(Thread.currentThread().getName()+" 取钱成功,吐出钞票:"+drawAmount); balance-=drawAmount; System.out.println("余额:"+(balance-drawAmount)); }else{ System.out.println("取钱失败,余额不足!"); } } public String getAccountNo() { return accountNo; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public double getBalance() { return balance; } @Override 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; } @Override 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; } }
package com.synchronized3; public class DrawMoney implements Runnable { private Account account; private double drawAmount; public DrawMoney(Account account,double drawAmount) { this.account = account; this.drawAmount = drawAmount; } @Override public void run() { //同步方法的同步不监视器是this,this代表draw方法的对象(Account对象) account.draw(drawAmount); } }
package com.synchronized3; public class DrawMain { public static void main(String[] args) { Account ac=new Account("111", 1000); new Thread(new DrawMoney( ac, 800)).start(); new Thread(new DrawMoney(ac, 800)).start(); } }
Thread-0 取钱成功,吐出钞票:800.0 余额:-600.0 取钱失败,余额不足!