线程安全问题

文章目录

      • 问题引入
        • 代码
      • 线程安全问题出现的原因是什么?
      • 方法一:同步代码块
        • 改进
      • 方法二:同步方法
      • 方法三:lock

问题引入

线程安全问题_第1张图片

不考虑线程同步程序运行后的结果:

小明取钱100000.0
小红取钱100000.0
小明取完后,余额:0.0
小红取完后,余额:-100000.0

银行亏钱了?嘿嘿,我有一个大胆的想法

代码

class Test {
    public static void main(String[] args) {
        account acc = new account("ICBC-110",100000);
        new drawThread(acc,"小明").start();
        new drawThread(acc,"小红").start();
    }
}


class account {
    private String ID;
    private double money;

    public account() {
    }

    public account(String ID, double money) {
        this.ID = ID;
        this.money = money;
    }

    public String getID() {
        return ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public void drawMoney(double money) {
        //区分谁来取钱
        String name = Thread.currentThread().getName();

        //取钱步骤
        if (this.money >= money) {
            System.out.println(name+"取钱"+money);
            this.money -= money;
            System.out.println(name+"取完后,余额:"+this.money);
        }else {
            System.out.println(name +"来取钱,余额不足~");
        }
    }
}

 class drawThread extends Thread{
    private account acc;
    public drawThread(account acc,String name)
    {
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱
        acc.drawMoney(100000);
    }
}

线程安全问题出现的原因是什么?

  • 存在多个线程在同时执行:两个人同时取钱
  • 同时访问一个共享资源:同一个账户
  • 存在修改该共享资源:修改余额

那我们应该怎么解决这一问题呢。

方法一:同步代码块

synchronized 就是一个锁,每当一个线程运行到这里时会给这个线程加锁,然后才能执行同步代码块里面的代码,执行完后再进行解锁,让下一个进行重复当前步骤。

    public void drawMoney(double money) {
        //区分谁来取钱
        String name = Thread.currentThread().getName();

        synchronized ("取钱") {
            //取钱步骤
            if (this.money >= money) {
                System.out.println(name+"取钱"+money);
                this.money -= money;
                System.out.println(name+"取完后,余额:"+this.money);
            }else {
                System.out.println(name +"来取钱,余额不足~");
            }
        }
    }
改进

"取钱"是一个字符串,在常量池里面,就一份,假如又多了俩人小黑和小白,这俩人操作的是同一个账户,那这样的话定义成 “取钱” 这一对象就不能达到想要效果(执行效果是小红或者小明取钱的时候小黑或者小白也不能取钱)

    public void drawMoney(double money) {
        //区分谁来取钱
        String name = Thread.currentThread().getName();

        synchronized (this) {
            // 这里改成this,原来的字符串防的是所有的,比如小红取,小黑、白也不能取![请添加图片描述](https://img-blog.csdnimg.cn/5c8eba6dfa7f40e7ac2ce2ab79057669.png)

            //取钱步骤
            if (this.money >= money) {
                System.out.println(name+"取钱"+money);
                this.money -= money;
                System.out.println(name+"取完后,余额:"+this.money);
            }else {
                System.out.println(name +"来取钱,余额不足~");
            }
        }
    }

方法二:同步方法

线程安全问题_第2张图片

   public synchronized void drawMoney(double money) {
        //区分谁来取钱
        String name = Thread.currentThread().getName();
        //取钱步骤
        if (this.money >= money) {
            System.out.println(name+"取钱"+money);
            this.money -= money;
            System.out.println(name+"取完后,余额:"+this.money);
        }else {
            System.out.println(name +"来取钱,余额不足~");

        }
    }

方法三:lock

private Lock lk = new ReentrantLock();

每个账户对象都应该有一个锁对象。方便锁住访问同一对象的线程

     public void drawMoney(double money) {
        //区分谁来取钱
        String name = Thread.currentThread().getName();
        lk.lock();
        try {
            //取钱步骤
            if (this.money >= money) {
                System.out.println(name+"取钱"+money);
                this.money -= money;
                System.out.println(name+"取完后,余额:"+this.money);
            }else {
                System.out.println(name +"来取钱,余额不足~");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lk.unlock();
        }
    }

你可能感兴趣的:(java,java,线程同步)