神仙文!带你探究,并发编程问题源头

并发编程是开发中无法规避的问题,而且在并发场景下经常会出现一些很”诡异”的问题。为了搞清楚并发,需要明确出现并发问题的源头

在计算机中cpu、缓存、I/O设备这个三者之间速度的差异一直存在问题,cpu>缓存>I/O设备。按照串行的逻辑,程序的整体速度取决速度最慢的I/O设备。

为了提高程序的整体性能,做了如下的升级

  • 增加cpu缓存,平衡cpu和内存的速度差异
  • 操作系统增加多进程、多线程;通过分时复用平衡CPU和I/O设备间的速度差异
  • 编译器指令优化,可以充分利用缓存,提高执行效率

但是凡事有利必有弊,性能提高的同时,也会引发一些问题。并发编程中的问题源头就是因为这个导致的

缓存导致了可见性问题

目前操作系统都是多核的,cpu都有自己的缓存,这个时候就需要考虑数据的一致性问题。某一个变量被多个线程操作;相互的操作是不可见的。这个时候就会出现问题了,这就是可见性问题

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

现代操作系统都是基于线程调度的,java并发程序出现的多线程,会涉及到线程切换,在一条语句可能需要多个cpu指令完成。例如代码count+=1大概需要三条指令。

  • 把变量 count 从内存加载到CPU的寄存器中
  • 在寄存器中把变量 count + 1
  • 把变量 count 写入到内存(缓存机制导致可能写入的是CPU缓存而不是内存)

操作系统做任务切换,可以发生在任何一条CPU指令执行完,所以并不是高级语言中的一条语句,不要被 count += 1 这个操作蒙蔽了双眼。假设 count = 0 ,线程A执行完 指令1 后 ,做任务切换到线程B执行了 指令1、指令2、指令3 后,再做任务切换回线程A。我们会发现虽然两个线程都执行了 count += 1 操作。但是得到的结果并不是2,而是1。

如果 count += 1 是一个不可分割的整体,线程的切换可以发生在 count += 1 之前或之后,但是不会发生在中间,就像个原子一样。 我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性

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

有序性是指程序按照代码的顺序执行。编译器为了优化性能,可能会改变代码的执行顺序。如: a = 1; b = 2; ,编译器可能会优化成: b = 2; a = 1 。在这个例子中,编译器优化了程序的执行先后顺序,并不影响结果。但是有时候优化后会导致意想不到的Bug。

在单例模式的双重检查创建单例对象中。如下代码:

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

new Singletion() 这行代码可能出现问题,我们以为的执行顺序应该是这样的:

  • 分配一块内存m
  • 在内存m中实例化singleton对象
  • instance变量执行内存地址m

但是实际优化后的执行路径确实这样的:

  • 分配一块内存m
  • instance变量执行内存地址m
  • 在内存m中实例化singleton对象

如果的是这样的话就会有问题了呀

当线程A执行完了指令2后,切换到了线程B,

线程B判断到 if (instance != null) 。直接返回 instance ,但是此时的 instance 还是没有被实例化的啊!所以这时候我们使用 instance 可能就会触发空指针异常了。如图:

总结

遇到问题,定位到问题的根本原因才好解决问题。并发编程 只要我们能够深刻理解可见性、原子性、有序性在并发场景下的原理,很多并发 Bug 都是可以理解、可以诊断的 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值