学并发从问题溯源开始

2019-12-20  本文已影响0人  rock_fish
硬件速度差异问题

并发问题的本质是硬件设备速度问题: cup/内存/io 的速度有差异。
为了解决速度差的问题, 计算机体系机构、操作系统、编译程序都分别提供了“药物”(解决方案):

  1. 操作系统增加了进程/线程,以分时复用cpu,进而均衡cup/io设备的速度差异
  2. cpu增加缓存,以均衡与内存的速度差异
  3. 优化指令次序,使得运算更快
“药物”的三个副作用
  1. 原子性 问题
    一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为“原子性”;分时复用cpu,常有这种现象:一个线程对一个共享变量进行操作,还没有执行完毕就被剥夺了执行权,被中断了就有非原子性问题。
    通畅我们说的原子性操作是由 CPU指令来保证的,而不是高级语言中提供的操作符。 CPU指令级的原子操作太有限,不满足我们的需求,因此在高级语言层面提供了其他方式来解决非原子性问题(如锁)。

  2. 可见性 问题
    内存到CPU缓存,CPU缓存到内存的时机是不确定的; 由于数据变更不同步,就会导致缓存可见性问题

  3. 有序性 问题

为了提高性能,编译器和处理器常常会对指令进行重排序。一般重排序可以分为如下三种:


image.png

至此应该明白这些问题不是java语言独有的。

Java中的有序性可以概括成一句话: 如果再本线程内观察,所有的操作都是有序的;如果再一个线程中观察另一个线程,所有的操作都是无序的。 前半句是指线程内表现为串行的语义(Within Thread As-If-Serial Semantics),后半句是指指令重排序现象和工作内存与主内存同步延迟现象。 注意:指令重排序在任何时候都有可能发生,与是否为多线程无关,之所以在单线程下感觉没有发生重排序,是因为线程内表现为串行的语义的存在

线程间的通信和同步问题-溯源JMM

从上图可知,通过消息传递或者共享内存都可以实现 线程间的通信和同步,不同的语言可能处理方式不同。

java采用的是共享内存的方式,为了屏蔽各个硬件平台和操作系统对内存访问机制的差异化,提出了Java内存模型-JMM。

JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。

小结

处理并发本质上是要解决三个副作用:原子性问题,可见性问题,重排序问题。JMM屏蔽了底层的复杂多样又难易理解的东西,以更亲民的方式提供给我们解决这些问题的规范和工具。

万事皆有因果,理顺了,后续学习会更通畅。


返回并发目录


参考:https://www.jianshu.com/p/d52fea0d6ba5

上一篇下一篇

猜你喜欢

热点阅读