并发编程三大核心问题的诞生:可见性、原子性和有序性

可见性

CPU缓存导致的可见性问题

描述

可见性指的是一个线程对共享变量的修改,另一个线程能够立刻看到。单核心情况下,所有线程操作的都是同一个CPU的缓存,一个线程对缓存的写,对另一个线程来说是可见的,但是在多核心情况下,每颗CPU都有自己的缓存,当线程分别在不同的CPU上操作时,共享变量一致性问题就出现了,这时一个线程对共享变量的操作对另一个线程而言就不具备可见性

图解
在这里插入图片描述
在这里插入图片描述

原子性

线程切换带来的原子性问题

描述

原子性指的是一个或多个操作在CPU执行的过程中不被中断的特性

案例

count += 1。执行这条语句至少需要三条CPU指令:1,将count变量从内存(或缓存)加载到CPU寄存器;2,在寄存器中执行count + 1操作;3, 将结果写入内存(或缓存)。操作系统的任务切换可能会发生在任何一条CPU指令执行完成后,因此这条语句的原子性将得无法保证

图解

在这里插入图片描述
在这里插入图片描述

有序性

编译器优化带来的有序性问题

描述

有序性指的是程序按照代码的先后顺序执行

案例

双重检测锁创建单例对象。创建过程大致分解为以下步骤:1,判断instance是否为null;2,分配一块内存M;3,在内存M上初始化单例对象;4,将M的地址赋值给instance变量。但是经过编译器优化后,指令3和4的顺序将颠倒,会先将M的地址赋值给instance变量,然后在内存M上初始化单例对象。如果当CPU指令执行完步骤3后切换为其他线程,则其他线程可能会返回未初始化的instance实例,这时访问就可能会触发空指针异常

代码

public class SingletonDemo {
    static SingletonDemo instance;
    static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }
}

图解

在这里插入图片描述

补充

CPU、内存、I/O设备之间的速度差异。CPU增加缓存,以均衡与内存间的速度差异,操作系统增加了进程、线程以分时复用CPU,进而均衡CPU与I/O设备间的速度差异,编译程序优化指令执行次序,使得缓存能够得到更加合理的利用

[参考资料] Java并发编程实战

发布了18 篇原创文章 · 获赞 15 · 访问量 355

猜你喜欢

转载自blog.csdn.net/momo57l/article/details/104355068