线程同步问题

Posted on 2017-02-10 10:59  fkr  阅读(213)  评论(0编辑  收藏  举报

  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("\t余额为:"+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("\t余额为:"+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("\t余额为:"+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);
    }
}