线程安全问题

文章目录

问题引入

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

小明取钱100000.0

小红取钱100000.0

小明取完后,余额:0.0

小红取完后,余额:-100000.0

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

代码
java 复制代码
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 就是一个锁,每当一个线程运行到这里时会给这个线程加锁,然后才能执行同步代码块里面的代码,执行完后再进行解锁,让下一个进行重复当前步骤。

java 复制代码
    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 +"来取钱,余额不足~");
            }
        }
    }
改进

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

java 复制代码
    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 +"来取钱,余额不足~");
            }
        }
    }

方法二:同步方法

java 复制代码
   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

java 复制代码
private Lock lk = new ReentrantLock();

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

java 复制代码
     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();
        }
    }
相关推荐
陌殇殇30 分钟前
002 SpringCloudAlibaba整合 - Feign远程调用、Loadbalancer负载均衡
java·spring cloud·微服务
猎人everest1 小时前
SpringBoot应用开发入门
java·spring boot·后端
山猪打不过家猪3 小时前
ASP.NET Core Clean Architecture
java·数据库·asp.net
AllowM3 小时前
【LeetCode Hot100】除自身以外数组的乘积|左右乘积列表,Java实现!图解+代码,小白也能秒懂!
java·算法·leetcode
不会Hello World的小苗4 小时前
Java——列表(List)
java·python·list
二十七剑5 小时前
jvm中各个参数的理解
java·jvm
东阳马生架构6 小时前
JUC并发—9.并发安全集合四
java·juc并发·并发安全的集合
计算机小白一个7 小时前
蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
java·数据结构·算法·蓝桥杯
菠菠萝宝7 小时前
【Java八股文】10-数据结构与算法面试篇
java·开发语言·面试·红黑树·跳表·排序·lru
不会Hello World的小苗7 小时前
Java——链表(LinkedList)
java·开发语言·链表