线程安全问题

文章目录

问题引入

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

小明取钱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();
        }
    }
相关推荐
星河梦瑾33 分钟前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富37 分钟前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想38 分钟前
JMeter 使用详解
java·jmeter
言、雲41 分钟前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
Yvemil71 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。1 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文2 小时前
宠物管理系统:Dao层
java·开发语言·宠物
王ASC2 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web