生活

喝着舍友打的豆浆,贼爽,所以继续学习咯!!!

————

在JAVA的并发编程中,有一个很重要的东西需要搞清楚,那就是内存模型。

内存模型的目标:

定义了程序中变量的访问规则,以及虚拟机存储变量到内存,从内存中取出变量的底层细节。
这里所说的变量不包括局部变量、方法参数等线程私有的变量,这些变量本身不存在竞争问题,自然也无需再多线程的开发中研究。

内存模型细节:

JVM规定了所有的变量都存储在主内存中,每一个线程有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存的变量的拷贝副本,线程对变量的所有操作(读取 赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的工作内存不能直接访问到对方工作内存中的变量,线程之间值得传递只能通过主内存去交换变量。

在这里插入图片描述

线程1和线程2想要进行数据交换必须经历以下两个步骤:
1、线程1把其工作内存中的更新过的共享变量刷新到主内存中去
2、线程2到主内存读取这个变量,并copy一份副本到自己的工作内存

内存中的交互操作

关于主内存和工作内存之间的交互协议,其实就是从主内存同步数据到工作内存以及从工作内存同步数据到主内存的实现细节:
lock(锁定):作用于主内存,标记一个变量被一个线程独占
unlock(解锁):作用于主内存,标记一个变量被一个线程释放,允许其他线程独占
read(读取):作用于主内存的变量,指把一个变量读取出来并传输到工作内存,以便后面的load
load(载入):作用于工作内存的变量,把从主内存读取出来的变量刷新到工作内存的副本中
use(使用):作用于工作内存的变量,当要使用这个变量时,把这个变量传输给执行引擎
assign(赋值):作用于工作内存的变量,当要对这个变量进行赋值操作时,由执行殷勤将这个更新后的变量值赋值给这个变量
store(存储):作用于工作内存的变量,把更新后的变量传输到主内存,方便后面的write操作
write(写入):作用于主内存,把工作内存中更新后的变量,刷新到主内存去

如果要一个变量从主内存复制到工作内存,需要按顺序执行read、load命令;
如果要把一个变量从工作内存写回主内存,需要按顺序执行store、write命令。
java内存模型只要求上述操作必须按顺序执行,但是并不要求连续执行。也就是read load / store write命令之间可以插入其他命令。
比如一个线程读取主内存中的变量a和变量b时 可以的操作是read a read b load a load b

JAVA的内存模式还规定了上述的8种操作,必须满足以下规则:
1、不允许read load / store write 命令单独出现
2、不允许一个线程丢弃最近的assign操作,也就是在工作内存中修改了必须同步回主内存中
3、不允许一个线程在没有assign操作的情况下,把工作内存的数据同步回主内存
4、一个新的变量只能在主内存中诞生,不允许工作内存中使用一个未被初始化的变量。对一个变量use和store之前,必须要先load和assign
5、一个变量在同一个时刻。只允许一个线程进行lock()操作,而且lock和unlock必须一起操作
6、一个变量被lock时,将会清空该工作内存中这个变量的值,在执行引擎使用这个变量之前,必须要先load或者assign
7、一个线程没有执行过lock操作,就不能对他unlock,也不能unlock一个其他线程锁定的变量、。。。感觉是废话
8、对一个变量执行unlock之前,必须把该变量同步回主内存,执行store/write操作

重排序

在执行程序时为了提供性能,编译器和处理器经常会对指令进行重排序,
重排序分为以下三种类型:
1、编译器优化的重排序。编译器在不改变单线程语义的前提下,可以重新安排语句的执行顺序。
2、指令级并行重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3、内存系统的重排序。由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

从Java源代码到最终实际执行的指令序列,会经过下面三种重排序:

在这里插入图片描述

为了保证内存的可见性,Java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。Java内存模型把内存屏障分为LoadLoad、LoadStore、StoreLoad和StoreStore四种:
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐