• 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    预期结果: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……");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

    三、原子性

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

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

    四、内存可见性

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

    图解:

    代码:

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    结果:

    五、指令重排序

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

    图解:

    代码:

    Test t = new Test();
    
    • 1

    原因:
    在这里插入图片描述

  • 相关阅读:
    [CC2642R1][VSCODE+Embedded IDE+Cortex-Debug] TI CC2642R1 快速搭建VsCode开发环境
    Redis 分布式锁过期了,还没处理完怎么办?
    手写一个SpringBoot组件发布到Maven中央仓库
    【php详细笔记】上传文件到服务器
    1.3操作系统运行环境-拓展知识
    使用Kohya_ss训练Stable Diffusion Lora
    如何通过百度SEO优化提升网站排名(掌握基础概念,实现有效优化)
    LeetCode 之 有序数组的平方
    C++ 多态
    图书管理系统
  • 原文地址:https://blog.csdn.net/qq_59854519/article/details/126670075