jvm学习篇02 - Java内存模型

还是《深入理解Java虚拟机》的一些读后总结。

这一部分主要涉及到“如何高效并发”的问题,虚拟机如何实现在多线程环境下,解决各个线程因为共享和竞争数据而带来的一系列问题。

高速缓存与指令重排序

为了充分利用CPU资源:
(1)由于计算机的内存读写速度与CPU的运算速度存在几个量级的差距,因此在内存与CPU之间引入了读写速度接近于CPU的高速缓存作为缓冲。
内存将需要运算的数据复制到缓存中,让运算能快速进行;CPU在运算完之后将结果复制到缓存中,再同步回主存。这样CPU就无需等待缓慢的内存读写了。
每个处理器都有自己的高速缓存,而且他们共享同一主内存,这样就可能产生缓存一致性的问题,为了解决这个问题,各个处理机在访问主内存时,需要去遵循一些缓存一致性协议。
(2)除了高速缓存,处理机为了提高各个运算单元的利用率还会对输入代码进行乱序执行优化。类似的,Java虚拟机的即时编译器也有指令重排序优化。乱序优化会保证最终计算结果一致,但是代码执行顺序却不一定按先后顺序。

Java内存模型

概念

Java内存模型的主要目的是定义了程序中各个变量的访问规则,即Java虚拟机中如何将变量存储到主内存中和如何从主内存中取出变量。
Java内存模型规定所有的变量都存储在主内存中,每条线程都拥有自己的工作内存,线程的工作内存中保存了被该线程使用的变量的主内存副本,所有线程对变量的操作(读取、赋值)都必须在自己的工作内存中进行,而不能直接读写主内存中的数据。线程也不能去访问其他线程工作内存中的变量,线程之间的变量的传递需要通过主内存来完成。

内存间交互操作

Java内存模型中定义了八种原子性操作来将一个变量从主内存中拷贝到工作内存和将工作内存中的变量同步回主内存中。

  1. lock :锁定,作用于主内存中的变量,将一个变量标识为一个线程独占的状态。
  2. unlock:解锁,作用于主内存中的变量,将处于锁定状态的变量释放。
  3. read:读取,作用于主内存中的变量,将一个变量从主内存传输到线程的工作内存中供后续load操作使用。
  4. load:载入,作用于工作内存中的变量,它会将read操作从主内存中读取的变量放入到工作内存的变量副本中。
  5. use:使用,作用于工作内存中的变量,它会将工作内存中的变量传递给执行引擎。(每当虚拟机执行到需要使用变量的值的字节码指令时就会执行本操作)
  6. assign:赋值,作用于工作内存中的变量,它会把执行引擎传递过来的值赋给工作内存中的变量。(每当虚拟机执行到一个将值赋值给变量的字节码指令时就会执行本操作)
  7. store:存储,作用于工作内存中的变量,它会将工作内存中的变量传输到主内存中,以供后续write操作使用
  8. write:写入,作用于主内存中的变量,它把store操作从工作内存中得到的变量写入到主内存的变量中

先行发生原则

先行发生原则定义了两个操作的偏序关系,符合以下规则的操作就有顺序性保障,否则虚拟机就会对其进行指令重排序。

1、程序次序规则。在一个线程内,按照控制流的顺序,书写在前面的代码先行发生于后面的。
2、Volatile变量规则。对volatile修饰的变量的写操作先行发生于对它的读操作。
3、线程启动规则。Thread对象的start()方法先行发生于此线程的每一个动作。
4、线程终止规则。线程的所有操作都先行发生于对此线程的终止检测。
5、线程中断规则。对线程interrupt()方法的调用先行发生于被中断线程的代码所检测到的中断事件。
6、对象终止规则。一个对象的初始化完成(构造函数执行结束)先行发生于发的finilize()方法的开始。
7、传递性。A先行发生B,B先行发生C,那么,A先行发生C。
8、管程锁定规则。一个unlock操作先行发生于后面对同一个锁的lock操作。

需要注意的是,一个操作“先行发生”,不代表这个操作一定是“时间上的先发生”,例如

//其中,根据程序次序规则,操作1先行发生于操作2,但是两个操作是不存在依赖关系的,因此可能被虚拟机重排序执行,导致操作2先执行,但是这并不影响先行发生原则的正确性,因为我们仍能得到正确的结果,而且也无法感知到。
int i = 10//操作1
int j = 20;//操作2

结论:先行发生原则和时间顺序之间基本没有因果关系,所以衡量并发安全问题时不要受时间顺序影响,一切以先行发生原则为准。

volatile特殊规则

volatile提供Java最轻量级的同步机制,但是不保证原子性。当一个变量被修饰为volatile时,会被赋予两项特性:

  1. 保证该变量的内存可见性,即该关键字可以保证在一个线程中对共享变量的更新,在其他线程中是立即可见的。volatile关键字会保证对变量的修改不会缓存在工作内存中,而是直接刷新回主内存;而且在读取变量时,不会使用工作内存中的值,而是去主内存中获取最新值。
  2. 禁止指令重排序。保代码的执行顺序和程序顺序一致。

synchronized

● synchronized也可以用来保证共享变量内存可见性。
来自《Java并发编程之美》

进入synchronized内存语义:把在synchronized块中使用到的变量从工作内存中清除,这样在使用到该变量时就不会在工作内存中获取,而是从主内存中去获取最新值;
退出synchronized块的内存语义:把synchronized块中对共享变量的修改刷新回主内存中。

● synchronized也可以用来禁止指令重排序。

禁止指令重排优化是利用“一个变量在同一时刻仅允许一个线程对其进行lock操作”这条规则获得,这条规则决定了持有同一个锁的同步块只能串行的进入

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值