多线程带来的风险——线程安全问题

线程安全问题

解决线程安全问题

1.首先观察线程不安全问题

  • 如下代码:本来预期结果n = 0;但是因为线程的安全问题,导致会出现其他结果,只有有不是预期结果的情况出现,我们就不能说它是安全的;
  • 因为n++和n–的时候不具备原子性
package 线程安全问题;

/**
 * 这样线程存在很大的安全问题,因为结果不一定是预期结果
 */
public class ThreadDemo {
    
    
    private static int n = 0;
    private static final int COUNT = 100000;
    static class Adder extends Thread{
    
    
        @Override
        public void run() {
    
    
            for (int i = 0;i < COUNT;i++){
    
    
                n++;
            }
        }
    }

    static class Suber extends Thread{
    
    
        @Override
        public void run() {
    
    
            for (int i = 0;i < COUNT;i++){
    
    
                n--;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        Thread t1 = new Adder();
        Thread t2 = new Suber();

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(n);
    }
}

2.线程不安全原因

1.原子性

  • 单一一条指令一定具备原子性——中间不会出现线程切换
  • 多条指令之间发生了切换,也不会产生副作用;

比如刚才我们看到的 n++(就不具备原子性),其实是由三步操作组成的:

  1. 从内存把数据读到 CPU;
  2. 进行数据更新;
  3. 把数据写回到 CPU;
常见具有原子性的情况
//下面的这几个是具备原子性的
int a = 10; 
boolean a =true;
byte/short/char/float 
(因为JVM是以32 bit作为基本单位进行操作的)
//下面这俩个是不具备原子性的
long a = 10;
double a = 10.0;

2.可见性

  • 为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线程之间不能及时看到改变,这个就是可见性问题。

3.代码顺序性(重排序)

1.什么是代码重排序

  • 一段代码是这样的:
  1. 去前台取下 U 盘
  2. 去教室写 10 分钟作业
  3. 去前台取下快递
  • 如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台。这种叫做指令重排序

2.代码重排序会给多线程带来的问题?

  • 刚才那个例子中,单线程情况是没问题的,优化是正确的,但在多线程场景下就有问题了,什么问题呢。可能快递是在你写作业的10分钟内被另一个线程放过来的,或者被人变过了,如果指令重排序了,代码就会是错误的。

3.关于线程安全问题

  • 1.线程之间没有数据共享一定不会有线程安全问题
  • 2.即使有数据共享,但是只要没有修改操作也不一定会有线程安全问题。

猜你喜欢

转载自blog.csdn.net/qq_45665172/article/details/113816086