Java内存模型—JMMhappens-before规则
有时候编译器、处理器的优化会导致runtime与我们设想的不一样,为此Java对编译器和处理器做了一些限制,JAVA内存模型(JMM)将这些抽象出来,这样编写代码时就无需考虑那么多底层细节,并保证“只要遵循JMM的规则编写程序,其运行结果一定是正确的”。
JMM的抽象结构
在Java中,所有的实例、静态变量存储在堆内存中,堆内存是可以在线程间共享的,这部分也称为共享变量。而局部变量、方法定义参数、异常处理参数是在栈中的,栈内存不在线程间共享。
而由于编译器、处理器的优化,会导致共享变量出现可见性问题,像在多核处理器中(multi-processor),线程可以在不同的处理器上执行,而处理器之间缓存不一致,会使共享变量出现可见性问题,有可能两个线程看到同一个变量不同值。
JMM将这些硬件做的优化抽象成每个线程都有一个本地内存。需要读写共享变量时,从主内存中拷贝一份到本地内存。当写共享变量时,先写到本地内存中去,在将来某个时间再刷新到主内存中。当再次读共享变量时,则只会从本地内存中读取。
这样线程间通讯就需要经过两步:
- 写线程:刷新本地内存到主内存中去
- 读线程:从主内存读取更新后的值
这样在写-读之间就有一个延迟:本地内存什么时候刷新到主内存中去?导致可见性问题,不同线程可能看到的共享变量不一样。
happens-before
从字面上看happens-before的意思是“发生在此之前”。这是java对程序执行顺序制定的规则,实现同步必须遵循该规则。这样程序员只需要写出正确的同步程序,happens-before保证运行结果不会错。
A happens-before B,不仅仅表示A在B之前执行,还意味着A的执行结果对B可见,这保证了可见性。
A happens-before B,A也不一定要在B之前执行,如果AB交替,执行结果任然正确,则允许编译器、处理器进行优化重排序。所以只要程序结果正确,编译器、处理器怎么优化,怎么重排序都没问题,都是好的。
happens-before规则
- 程序顺序规则:在一个线程中,前面的操作happens-before后面的操作
- 锁规则:对同一个锁,解锁happens-before加锁
- volatile域规则:写volatile变量,happens-before后面任意一个读这个volatile变量的操作
- 传递性:A happens-before B,B happens-before C,则A happens-before C
- start()规则:如果线程A执行ThreadB.start() 那么ThreadB.start() happens-before 线程B中任何操作
- join()规则:如果线程A执行ThreadB.join(),那么线程B中的所有操作happens-before ThreadB.join()
下面这个示例有助于理解happens-before
double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r *r; //C
这里有三个happens-before关系,规则1、2是程序顺序规则,规则3是传递性规则推导出来的:
- A happens-before B
- B happens-before C
- A happens-before C
C依赖于A、B,但是A和B谁也不依赖。所以即使A和B重排序,执行结果也不会发生变化,这种重排序,JMM是运行的。
下面两种执行顺序的结果都是正确的。
- Oracle基础知识-SQL简单命令
- 【java开发系列】—— spring简单入门示例
- JavaMelody应用监控使用指南
- 记录安装oracle的那些事(四)之oracle 缺包安包
- 【面试虐菜】—— JAVA面试题(1)
- JAVA增删改查XML文件
- javascript去掉字符串前后空格
- 构建通用的 React 和 Node 应用
- 有趣的 CSS 像素艺术
- 【插件开发】—— 11 窃听风云(Java事件监听原理-GEF实例讲解)
- 【面试虐菜】—— JAVA面试题(2)
- JavaMelody监控spring、struts
- 《JavaScript语言精粹》—— 读书总结
- 视差滚动技术的简介及运用
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- centos7卸载自带jdk并安装新的jdk
- 即使不懂单元测试,会用这个工具也够了~
- 统计分布讲解
- 使用PyTorch Lightning自动训练你的深度神经网络
- linux防火墙相关配置命令及操作(centos和ubuntu)
- 带你吃透Spring事务7种传播行为 | Spring第45篇
- Linux如何查看端口被占用情况?Windows如何查看端口被占用情况?
- centos7安装Docker详细步骤(无坑版教程)
- Java的Object类详解(入门必备)
- Java的包机制以及import、static import的用法详解(看这篇就够了)
- AIM2020 Efficient Super Resolution: Methods and Results
- hive的group by与distinct的区别及性能测试比较
- hive的order by操作
- centos安装mysql-server报错:No package mysql-server available.
- Java的内部类详解(结合代码全面分析)