Java-线程不安全的原因(图解)

一、多线程修改同一个变量

 count自增100_0000次,并发执行:

count++实际由3部分组成:

  • 从内存读数据到cpu(load),
  • cpu寄存器,进行自增操作(add),
  • cpu寄存器又返回数据到内存(save).

图解:

代码:

class Counter {
 
    public int count = 0;

    public void incerse() {
 
        count++;
    }
}
public class Thread2 {
 
    public static void main(String[] args) {
 
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
 
            for (int i = 0; i < 50_0000; i++) {
 
                counter.incerse();
            }
        });
        Thread t2 = new Thread(() -> {
 
            for (int i = 0; i < 50_0000; i++) {
 
                counter.incerse();
            }
        });
        t1.start();
        t2.start();
        try {
 
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
 
            e.printStackTrace();
        }
        System.out.println(counter.count);
    }
}

预期结果:100_0000

实际结果:

注:

  • 单线程修改同一变量,安全;
  • 多线程读同一变量,安全;
  • 多线程修改不同变量,安全.

二、抢占式

 各线程之间是抢占式执行,程序猿无法得知其执行的顺序.

代码:

public class Thread3 {
 
    public static void main(String[] args) {
 
        Thread t1 = new Thread(() -> {
 
            System.out.println("t1……");
        });
        t1.start();
        System.out.println("main……");
    }
}

预期结果:先执行t1……,再执行main……

实际结果:

原因:操作系统内核的随机调度,程序猿无法干预.

三、原子性

 不可拆分的 最小单位 就是原子.

原子:
 a = 10; //一步到位 
非原子操作:
 b++; //上述3步操作

四、内存可见性

一个线程读,一个线程写,很容易导致代码优化,产生误判,从而导致的不安全问题.

图解:

代码:

public class Thread4 {
 
    static int flag = 0;

    public static void main(String[] args) throws InterruptedException {
 

        Thread t1 = new Thread(new Runnable() {
 
            @Override
            public void run() {
 
                while (true) {
 
                    if(flag != 0) {
 
                        System.out.println("线程执行中……");
                        break;
                    }

                }
                System.out.println("线程执行结束");
            }
        });

        Thread t2 = new Thread(new Runnable() {
 
            @Override
            public void run() {
 
                try {
 
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
 
                    e.printStackTrace();
                }
                flag = 1;
            }
        });
        t1.start();
        t2.start();
    }
}

结果:

五、指令重排序

代码的执行顺序和逻辑顺序不一致,也是为了提升效率造成的.

图解:

代码:

Test t = new Test();

原因:

喜欢的话,记得点赞,关注+转发!!!

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章