java线程安全问题详解

一、什么时候数据在多线程并发的环境下会存在安全问题?

三个条件:

  • 条件1:多线程并发。
  • 条件2:有共享数据。
  • 条件3:共享数据有修改的行为。

满足以上3个条件之后,就会存在线程安全问题。

二、怎么解决线程安全问题?

        线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线程同步机制。

三、银行 取钱/存钱 案例

Account 类
package ThreadSafa;

/*
银行账户
 */

public class Account {
    // 账号
    private String actno;
    // 余额
    private double balance;

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    //取款方法
    public void withdraw(double money) {
        // 取款之前的余额
        double before = this.getBalance();
        // 取款之后的余额
        double after = before - money;
        // 更新余额
        try {
            //模拟网络延时 更新余额不及时 百分百会出问题
            Thread.sleep(1 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setBalance(after);
    }
}

AccountThread 类

package ThreadSafa;

public class AccountThread extends Thread {
    // 两个线程必须共享同一个账户对象。
    private Account act;

    //通过构造方法传递过来账户对象


    public AccountThread(Account act) {
        this.act = act;
    }

    @Override
    public void run() {
        double money = 5000;
        //取款
        act.withdraw(5000);

        System.out.println(Thread.currentThread().getName() + "账户" + act.getActno() + "取款成功,余额" + act.getBalance());
    }
}

Test 类

package ThreadSafa;

public class Test {
    public static void main(String[] args) {
        // 创建账户对象
        Account act = new Account("act-001", 10000);
        //创建两个线程
        Thread t1 = new AccountThread(act);
        Thread t2 = new AccountThread(act);
        //设置name
        t1.setName("t1");
        t2.setName("t2");
        //启动线程
        t1.start();
        t2.start();
    }
}

 运行问题

 解决方法  修改 Account 类  中的 withdraw 方法

package ThreadSafa;

/*
银行账户
 */

public class Account {
    // 账号
    private String actno;
    // 余额
    private double balance;

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    //取款方法
    public void withdraw(double money) {
        // 以下这几行代码必须是线程排队的,不能并发
        // 一个线程把这里的代码全部执行结束之后,另外一个线程才能进来

        /*
        线程同步机制的语法是:
            synchronized (){
                // 线程同步代码块
            }
            synchronized后面小括号中的这个“数据”是相当关键的。
            这个数据必须是多线程共享的数据。才能达到多线程排队

            ()中写什么?
                那要看你想让那些线程同步。
                假设t1、t2、t3、t4、t5,有5个线程,
                你只希望t1 t2 t3排队,t4 t5 不需要排队。怎么办?
                你一定要在()中写一个t1 t2 t3共享的对象。而这个
                对象对于t4 t5来说不是共享的。

             这里的共享对象是:账户对象
             账户对象是共享的,那么this就是账户对象吧!!!
             不一定是 this ,这里只要是多线程共享的那个对象就行。
         */

        synchronized (this) {
            // 取款之前的余额
            double before = this.getBalance();
            // 取款之后的余额
            double after = before - money;
            // 更新余额
            try {
                //模拟网络延时 更新余额不及时 百分百会出问题
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }
    }
}

四、总结

  • 一个对象一把锁
  • 线程拿到锁才能执行同步代码块代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

super码王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值