线程安全问题

文章目录

问题引入

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

小明取钱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();
        }
    }
相关推荐
276695829211 分钟前
tiktok 弹幕 逆向分析
java·python·tiktok·tiktok弹幕·tiktok弹幕逆向分析·a-bogus·x-gnarly
用户403159863966328 分钟前
多窗口事件分发系统
java·算法
用户403159863966331 分钟前
ARP 缓存与报文转发模拟
java·算法
小林ixn34 分钟前
大一新手小白跟黑马学习的第一个图形化项目:拼图小游戏(java)
java
nbsaas-boot1 小时前
Go语言生态成熟度分析:为何Go还无法像Java那样实现注解式框架?
java·开发语言·golang
hi0_61 小时前
03 数组 VS 链表
java·数据结构·c++·笔记·算法·链表
朝如青丝暮成雪_1 小时前
java的三大特征
java
用户0595661192091 小时前
Java 8 + 特性与 spring Boot 及 hibernate 等最新技术实操内容全解析
java·架构·设计
长安有故里y1 小时前
tomcat设置预防host头攻击
java·tomcat·firefox
生产队队长1 小时前
Tomcat问题:启动脚本startup.bat中文乱码问题解决
java·ajax·tomcat