java线程安全问题详解

Posted 我想月薪过万

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
        
    

四、总结

  • 一个对象一把锁
  • 线程拿到锁才能执行同步代码块代码

以上是关于java线程安全问题详解的主要内容,如果未能解决你的问题,请参考以下文章

Java线程池详解

Java 线程池详解

Java线程池详解

java线程安全问题详解

Java 高并发三 Java内存模型和线程安全详解

万字详解 Java 线程安全,面试必备!