jvm学习笔记9:java内存模型

1.存储设备与CPU执行效率的冲突以及解决方案

  • CPU进行计算时,需要与存储设备(内存)进行交互,存储和读取数据。由于计算机的存储设备的读取和写入速度和cpu运算速度有几个数量级的差距。这个时候,I/O速度成了执行效率的瓶颈。
  • 为了解决上面的问题,在存储设备和CPU之间加上了一层读写速度与CPU运算速度比较接近的高速缓存(Cache)作为缓冲。将运算需要使用的数据复制到缓存中,让运算能快速进行;当运算结束后能再从缓存同步回内存之中。
  • 但引入了高速缓存,又带来了新的问题。在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享一块主内存。当多个处理器的运算任务用到了同一块主内存的数据,那会导致各自的高速缓存中的数据与主内存中的数据不一致。同步回主内存的数据又以哪个高速缓存为准。这就是高速缓存带来的数据不一致问题。
  • 为了解决上面的缓存不一致问题,需要各个处理器在访问缓存时遵循一些协议,比如MSI、MESI、MOSI、Synapse、Firefly、Gragon等。可以去看博客学习下MESI协议,挺有意思的:并发研究之CPU缓存一致性协议(MESI)

在这里插入图片描述

2.Java内存模型

Java内存模型的主要目标是定义各个变量的访问规则。Java内存模型规定所有的变量都存储在主内存中(这里的主内存只是虚拟机内存的一部分,与上面提到的主内存是有区别的),每个线程都有自己的工作内存(可与上面的高速缓存类比),工作内存中保存了主内存的变量的主内存副本拷贝。
在这里插入图片描述

3. 内存间的交互操作

3.1 8个基本指令

上图中,工作内存和主内存之间怎么进行交互操作,怎么保存线程之间的数据一致性(类比我们之前提到过的缓存一致性协议)。Java内存模型定义了8种操作来完成,虚拟机实现时需要保证这八种操作是原子操作(对于double、long类型的变量,load、store、read、write操作在某些平台允许例外)

  1. lock(锁定):作用于主内存,它把一个变量标识为一条线程独占的状态;
  2. unlock(解锁):主用于主内存,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
  3. read(读取):作用于主内存,将一个变量的值从主内存传输到线程的工作内存,以便随后load使用;
  4. load(载入):作用于工作内存的变量,它把read操作得到的值放入工作内存的变量副本中;
  5. use(使用):作用于工作内存的变量,将工作内存一个变量的值传递给操作引擎;
  6. assign(赋值):作用于工作内存的变量,它将一个从执行引擎接收到的值赋给工作内存的变量;
  7. store(存储):作用于工作内存的变量,它把工作内存一个变量的值传送到主内存中,以便随后的write操作使用
  8. write(写入):作用于主内存的变量,它把store操作从工作内存得到的值
    在这里插入图片描述

3.2 8个基本指令的基本规则

java内存模型规定了8个原子指令在操作时必须遵循以下的规则:

  1. read和load、store和write操作不同单独出现;
  2. 不允许一个线程丢弃一个变量最近的assign操作,也就是在这个线程结束前,必须把工作内存改变了的变量同步回主内存;
  3. 不允许一个线程无原因(没有发生任何assign操作)的把数据从工作内存同步回主内存;
  4. 一个新的变量只能从主内存中诞生,不允许在工作内存中使用一个未被初始化(load或assign)的变量;
  5. 一个变量在同一时刻只允许一个线程对其进行lock操作,但lock操作能被同一线程执行多次,多次lock后,执行相同次数的unlock,变量才会被解锁;
  6. 如果对一个变量执行了lock操作,那么将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign;
  7. unlock操作前,必须有lock操作;也不允许去unlock其他线程lock的变量;
  8. unlock前,必须先把此变量同步回主内存(store、write操作)。

3.3 volatile关键字

掌握以下三个知识点:

  1. 保证了变量可见性;
  2. 禁止重排序;
  3. 没有实现原子性

底层是通过lock前缀指令实现的,关于lock前缀,参见volatile与lock前缀指令
lock指令的几个作用:

  • 锁总线,其它CPU对内存的读写请求都会被阻塞,直到锁释放,不过实际后来的处理器都采用锁缓存替代锁总线,因为锁总线的开销比较大,锁总线期间其他CPU没法访问内存
  • lock后的写操作会回写已修改的数据,同时让其它CPU相关缓存行失效,从而重新从主存中加载最新的数据
  • 不是内存屏障却能完成类似内存屏障的功能,阻止屏障两遍的指令重排序

4. 原子性、可见性、有序性

4.1 原子性

由Java内存模型来直接保证的原子性变量操作包括read、load、use、assign、store、write(大致可以认为基本数据类型的访问时具备原子性的,但实际long和double是非原子协定的,但基本可以忽略,hotspot虚拟机实现了long和double操作的原子性,只是虚拟机规范没有规定而已)
如果我们需要更大范围的原子性,可以使用lock和unlock执行。对应的就是synchrinized关键字的monitorentermonitorexit进行使用

4.2 可见性

可见性是指一个线程修改了共享变量的值,其他线程能够立即得知这个修改。volatilesynchronizedfinal能实现变量的可见性

4.3 有序性

Java程序中天然的有序性可以总结为:如果在本线程进行观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无须的。volatilesynchronized能保证有序性

5.Java中的各种

参见:java中的各种锁详细介绍

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值