博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程Bug源头:可见性、原子性和有序性问题
阅读量:6417 次
发布时间:2019-06-23

本文共 1838 字,大约阅读时间需要 6 分钟。

并发编程的起源

硬件设备发展的核心矛盾:CPU、内存、I/O设备三者间存在的速度差异。根据木桶原理,程序整体性能最终受制于速度最慢的I/O设备。

为了平和三者速度差异,计算机体系结构、操作系统、编译程序都做出了贡献,主要体现为:

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

编发编程出现问题的源头

一:缓存导致的可见性问题

单核时代,所有线程在同一CPU上云析,CPU缓存与内存的数据一致性容易解决。如下图,线程A与B操作同一个CPU里的缓存,故A修改过变量V后,B再访问变量V,得到的一定是最新值,即A修改过的值。

一个线程对共享变量的修改,另一个线程可以立即看到,称之为 可见性

多核时代,每个CPU都有各自的缓存,当多个线程在不同的CPU上执行时,这些线程操作的是不同的CPU缓存,如下图所示,线程A所修改的CPU-1缓存中的变量V,这个操作对线程B则不具有可见性。

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

高级语言里一条语句往往需要多条 CPU 指令完成,例如要完成count += 1,至少需要三条CPU指令。

  • 指令1:把变量count从内存加载到CPU的寄存器中;
  • 指令2:在寄存器中执行 +1 操作;
  • 指令3:将结果写入内存(缓存机制导致可能写入的是CPU缓存而不是内存)

操作系统进行线程切换,可以发生在任何一条CPU指令执行完(不是高级语言中的一条语句)。如下图所示,假设在线程A执行第一条CPU指令后发生了线程切换,A与B会以图中顺序执行。得到的count不是我们期望的2,而是1.

我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性成为原子性。CPU可以保证的原子操作是CPU指令级别,而高级语言层面保证操作的原子性。

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

有序性指的是程序按照代码先后顺序执行,而编译器为了优化性能,有时候会改变程序中语句的先后顺序。 举一个Java中的一个经典案例,双重检查的单例模式。

pubic class Singleto {    static Singleto instance;    static Singleto getInstance(){        if (instance == null) {            synchronized(Singleto.class) {                if (instance == null) {                    instance = new Singleton();                }            }        }    }    return instance;}复制代码

假设线程A、B同时调用getInstance()方法,乍一看上去,线程发现instance == null 后,会对Singleto.class加锁,JVM保证只有一个线程可以获得该锁,则另一个线程会处于等待状态。最后只有一个线程创建实例成功,另一个线程在锁释放后获得锁,然后检查instance == null时,发现Singleto实例已经创建成功,所以不会再创建一个Singleto实例。 实际上,getInstance()方法是存在问题的,问题就在new操作上,我们默认任务new操作会以以下顺序执行:

  • 1.在堆上分配一块内存M;
  • 2.在内存M上初始化Singleto对象的实例;
  • 3.把M的地址赋值给instance变量。

但经过优化后的执行顺序可能是这样的:

  • 1.分配一块内存M;
  • 2.将M的地址赋值给instance变量;
  • 3.最后在内存M上初始化Singleto对象。

假如线程A执行完指令2之后恰好发生了线程切换,切换到了线程B,B也执行getInstance()方法,则B会判断instance != null,所以直接返回instance,而此时instance还没有经过初始化,访问该变量会触发空指针异常。如下图所示。

总结

并发程序经常出现的问题归根结底是直觉欺骗了我们,要诊断并发Bug,需要深刻理解可见性、原子性、有序性在并发场景下的原理。

并发编程Bug源头: 缓存 带来的可见性问题; 线程 切换带来的原子性问题; 编译 优化带来的有序性问题。

转载地址:http://iqvra.baihongyu.com/

你可能感兴趣的文章
New Concept English Two 31 85
查看>>
New Concept English three (29)
查看>>
2014年发生的一些事情
查看>>
hdu3709
查看>>
自定义对话框,时间日期对话框
查看>>
windows上apache+php+mysql环境部署
查看>>
cocos2dx——裁剪节点ClippingNode
查看>>
13. Intellij IDEA调试功能使用总结
查看>>
3. Spring Boot Servlet
查看>>
3n+1问题
查看>>
[FPGA] DE0_NANO eeprom i2C控制程式
查看>>
java 反射之获取泛型对象的所有字段与对应的值(包括父类的)
查看>>
js中修改标签的hidden属性
查看>>
2012年流行的安卓手机浏览器
查看>>
树莓派摄像头直播程序,非常希望有贡献者一起玩
查看>>
mysql 用drop和delete方法删除用户的区别(草稿)
查看>>
vs code插件
查看>>
less学习笔记四
查看>>
如何在Ubuntu上安装LAMP服务器系统?
查看>>
Codeforces Round #547 (Div. 3) A.Game 23
查看>>