1.多个线程对同一个账户取钱的模拟操作(未加线程安全控制,则会出现非法操作)
(1)账户类
/** * * @author fengkuirui * @Date 2017-02-10 * 实现银行取钱问题; * 此类为账户类; */ public class Account { private String accountNo;//账户号 private double balance;//余额 public Account(){}; 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; } //重写hashCode方法 public int hashCode(){ return this.accountNo.hashCode(); } //重写equals方法 public boolean equals(Object obj){ if(this == obj){//如果是同一个堆; return true; } //如果不是同一个对象; //如果都是Account的对象; if(obj != null && obj.getClass() == Account.class){ Account account = (Account)obj; //判断账号是否一致; return account.getAccountNo().equals(this.accountNo); } return false; } }
(2)多线程取钱类
/** * * @author fengkuirui * @date 2017-02-10 * 多线程实现取钱操作; */ public class DrawThread extends Thread{ //模拟用户 private Account account; //当前所希望的取钱数; private double drawAmount; public DrawThread(){}; public DrawThread(String name,Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } //当多个线程修改一个共享数据时,将涉及数据的安全问题; public void run(){ //账户余额大于当前取的钱数可以取款 if(this.account.getBalance() >= this.drawAmount){ //吐钱; System.out.println(getName()+"取钱成功!吐出钞票:"+this.drawAmount); //修改余额 account.setBalance(this.account.getBalance() - this.drawAmount); System.out.println(" 余额为:"+this.account.getBalance()); }else{ System.out.println("余额不足"+getName()+"取钱失败!"); } } }
(3)测试类
/** * * @author fengkuirui * @date 2017-02-10 * 测试类 */ public class DrawTest { public static void main(String[] args) { //创建一个账户 Account account = new Account("123456",1000); //模拟两个线程对一个账户取钱; new DrawThread("甲",account,800).start(); new DrawThread("乙",account,800).start(); } }
(4)非法结果;
2.加入控制操作,防止出现非法操作
(1)只需要修改多线程取钱类即可,其他类保持不变;(同步代码块)
/** * * @author fengkuirui * @date 2017-02-10 * 多线程实现取钱操作; */ public class DrawThread extends Thread{ //模拟用户 private Account account; //当前所希望的取钱数; private double drawAmount; public DrawThread(){}; public DrawThread(String name,Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } //当多个线程修改一个共享数据时,将涉及数据的安全问题; public void run(){ //账户余额大于当前取的钱数可以取款 //使用account作为同步监视器,任何线程进入下面得同步代码块之前 //必须先获得对account账户的锁定,其他线程将无法获得该锁,也就是无法修改他 //这种做法符合:加锁——修改——释放锁; synchronized(this.account){ if(this.account.getBalance() >= this.drawAmount){ //吐钱; System.out.println(getName()+"取钱成功!吐出钞票:"+this.drawAmount); // try { // Thread.sleep(1); // } catch (Exception e) { // // TODO: handle exception // e.printStackTrace(); // } //修改余额 account.setBalance(this.account.getBalance() - this.drawAmount); System.out.println(" 余额为:"+this.account.getBalance()); }else{ System.out.println("余额不足"+getName()+"取钱失败!"); } } } }
(2)同步方法保证线程安全
在Account类中,添加一个取钱方法,改为同步
代码如下:
/** * * @author fengkuirui * @Date 2017-02-10 * 实现银行取钱问题; * 此类为账户类; */ public class Account { private String accountNo;//账户号 private double balance;//余额 public Account(){}; 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; } //重写hashCode方法 public int hashCode(){ return this.accountNo.hashCode(); } //重写equals方法 public boolean equals(Object obj){ if(this == obj){//如果是同一个堆; return true; } //如果不是同一个对象; //如果都是Account的对象; if(obj != null && obj.getClass() == Account.class){ Account account = (Account)obj; //判断账号是否一致; return account.getAccountNo().equals(this.accountNo); } return false; } public synchronized void draw(double drawAmount){ if(balance >=drawAmount){ //吐钱; System.out.println(Thread.currentThread().getName()+"取钱成功!吐出钞票:"+drawAmount); try { Thread.sleep(1); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } //修改余额 balance -= drawAmount; System.out.println(" 余额为:"+balance); }else{ System.out.println("余额不足"+Thread.currentThread().getName()+"取钱失败!"); } } }
package com.fkr.code12_2; /** * * @author fengkuirui * @date 2017-02-10 * 多线程实现取钱操作; */ public class DrawThread extends Thread{ //模拟用户 private Account account; //当前所希望的取钱数; private double drawAmount; public DrawThread(){}; public DrawThread(String name,Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } public void run(){ account.draw(drawAmount); } }