那些年读过的书《Java并发编程实战》十、再探究Java内存模型
1、什么是内存模型,为什么需要它?
(1)内存模型的发展背景
近几年计算性能通过重排序实现了很大的提升,而且处理器也越来越朝着多核处理器发展以实现硬件的并行性。随着处理器的不断强大,编译器也在不断的改进:通过指令重排序来实现优化执行,使用成熟的全局寄存器分配
算法。这些都使得线程在内存内的操作更趋于复杂,如果没有正确的同步机制下,内存间的操作呈现乱序执行,从而不能保证计算结果的正确性。
编译器重排序:使得编译后的指令顺序可以和源代码的顺序不一样
处理器重排序:在编译器重排序的基础上再进行指令的优化重排序
处理器的并行性:多线程内线程之间的操作执行顺序不同,要实现线程间的数据共享在没有同步的机制下易产生不安全的数据共享。
处理器的多级缓存:如果缓存提交至主内存的顺序没有一定的同步机制下,就会出现提交顺序的乱序,使得共享数据在内存的不可见性。

1)对单线程程序执行的影响
在指令重排序的情况下,只要保证最终的计算结果和严格的串行执行环境下的结果一致下,重排序等优化措施是可以的。
2)对多线程程序执行的影响
多线程中线程间各自的操作执行顺序不同,强行保持程序的串行性,只会增加线程间的调度次数,而频繁的线程调度会引起不要的上下文操作使得线程的开销很大也降低了程序的执行速度。在只需要进行数据共享的操作内使
用同步机制协调线程间的操作,实现线程间的数据共享(保存线程间短暂的串行性)就能够减少不要的性能开销。
(2)Java内存模型是处理器架构内存模型的抽象
Java内存模型屏蔽了不同处理器架构内存模型之间的差异(JVM通过插入内存栅栏来屏蔽JMM与底层平台内存模型之间的差异)
(3)重排序
1)进行重排序的条件:操作之间不存在偏序关系和全序关系
2)禁止重排序的方法:锁,volatile,final
重排序会破外操作之间的偏序关系(Happens-Before),从而产生数据竞争问题
(4)内存模型简介
1)简介:
Java内存模型是通过各种操作来定义的,包括对变量的读写操作,监视器的加锁和释放操作,以及现场的启动和合并操作。
Java内存模型实质是为最终计算结果的正确性,而采取的实现内存操作间保存偏序关系即实现内存操作的有序性,实现内存操作的内存可见性和原子性的内存访问操作的模型
2)偏序关系(Happens-Before):
偏序关系(Happens-Before)的本质:保证操作之间的内存可见性
保持偏序关系(Happens-Before)的准则:
规则名称
内容
说明
程序顺序规则 程序代码顺序自然保持操作间的偏序关系 监视器锁规则 在同一个锁上锁的释放肯定在锁的获取之后 volatile规则 volatile变量的写操作肯定在volatile变量的读操作之前 线程启动规则 线程的启动操作start肯定在线程执行操作之前 线程结束规则 线程执行的任何操作肯定在线程检测到该线程结束之前执行,或者从join操作或者调用Thread.isAlive是返回 中断规则 interrupt中断操作必须在线程检测到线程中断之前执行 终结器规则 对象的构造函数必须在对象的终结器执行之前执行 传递性 操作顺序的传递性
2.发布
(1)不安全发布的本质
不安全发布操作的本质就是:对象的发布操作和对象的访问操作之间缺乏偏序关系(Happens-Before排序)
除了不可变对象外,使用被另外一个线程初始化的对象通常都是不安全的,除非对象的发布操作是在使用该对象的线程开始使用之前执行(即保存对象的发布操作在对象的加载操作之前也就是两个操作间保持偏序关系)
(2)安全发布的本质
安装发布对象的本质就是:使用锁或者volatile关键字来禁止操作之间的重排序,从而保持操作之间的偏序关系
(3)安全的初始化模式
初始化的几种模式:
初始化模式 |
优点 |
缺点 |
备注 |
| synchronized加锁模式 | 禁止重排序,实现了线程安全的初始化 降低了初始化类或者创建实例的开销 |
数据竞争严重的情况下太耗性能 增加了访问被初始化延迟的字段的开销 |
不推荐使用 |
| 类初始化加锁模式 | 在类的静态初始化过程完成初始化,实现了线程安全的初始化 降低了初始化类或者创建实例的开销 |
仅适用于在构造时的状态,对于可变的对象读写操作之间仍然需要同步机制。仅限于静态字段的初始化 增加了访问被初始化延迟的字段的开销 |
类初始化期间获得一个锁,并且每个线程都至少获取一次这个锁以确保这个类已经加载。类初始化(静态初始化)时类在初始化阶段执行,在类型加载后被线程使用之前执行。 |
| 双重检测锁定模式(DCL) | 降低了因synchronize锁带来的性能消耗 降低了初始化类或者创建实例的开销 |
没有实现线程安全的初始化 | 错误的不安全的延迟初始化方案 |
| 基于volatile改良后的双重检测锁定模式(VDCL) | 降低了因synchronize锁带来的性能消耗,实现了线程安全的初始化 除了可用于静态字段也可以用于实例字段 降低了初始化类或者创建实例的开销 |
增加了访问被初始化延迟的字段的开销 |
无论通过类初始化加锁模式还是volatile关键字都是实现了禁止重排序或者实现操作间的偏序关系从而保证了操作内存的可见性。
3、初始化过程中的安全性
初始化安全性只能保证final修饰的值从构造过程完成时开始的可见性,对于非final的值,或者在构造完成后可以改变的值,必须采用同步来确保可见性
那些年读过的书《Java并发编程实战》十、再探究Java内存模型的更多相关文章
- 《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before
第16章 Java内存模型 终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻.这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略 ...
- 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
<Java并发编程实战>和<Java并发编程的艺术> Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...
- java并发编程实战《二》java内存模型
Java解决可见性和有序性问题:Java内存模型 什么是 Java 内存模型? Java 内存模型是个很复杂的规范,可以从不同的视角来解读,站在我们这些程序员的视角,本质上可以理解为, Java 内存 ...
- Java并发编程实战 第16章 Java内存模型
什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...
- Java并发编程(十四)Java内存模型
1.共享内存和消息传递 线程之间的通信机制有两种:共享内存和消息传递:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程 ...
- 《Java并发编程实战》文摘
更新时间:2017-06-03 <Java并发编程实战>文摘,有兴趣的朋友可以买本纸质书仔细研究下. 一 线程安全性 1.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何 ...
- Java并发编程实战——读后感
未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...
- 【Java并发编程实战】-----“J.U.C”:Semaphore
信号量Semaphore是一个控制访问多个共享资源的计数器,它本质上是一个"共享锁". Java并发提供了两种加锁模式:共享锁和独占锁.前面LZ介绍的ReentrantLock就是 ...
- 《Java并发编程实战》/童云兰译【PDF】下载
<Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...
随机推荐
- 什么是位、字节、字、KB、MB (转)
回顾一下按位操作符和移位操作符的知识,顺便复习一下位相关的基础知识. 位:"位(bit)"是电子计算机中最小的数据单位.每一位的状态只能是0或1. 字节:8个二进制位构成1个&qu ...
- Vue获取DOM元素的属性值
项目中需要做一个小弹层,如下图: 我需要知道点击元素距离顶部的值,再计算弹层的top值,如下图: 在vue中如何获取到DOM元素距离窗口顶部的值呢? 1.通过$event获取 html: <di ...
- Java知多少(33)多态对象的类型转换
这里所说的对象类型转换,是指存在继承关系的对象,不是任意类型的对象.当对不存在继承关系的对象进行强制类型转换时,java 运行时将抛出 java.lang.ClassCastException 异常. ...
- 【转】彻底搞清C#中cookie的内容
http://blog.163.com/sea_haitao/blog/static/77562162012027111212610/ 花了2天时间,彻底搞清C#中cookie的内容,搞清以下内容将让 ...
- MySQL数据库远程访问权限如何打开(两种方法)
在我们使用mysql数据库时,有时我们的程序与数据库不在同一机器上,这时我们需要远程访问数据库.缺省状态下,mysql的用户没有远程访问的权限. 下面介绍两种方法,解决这一问题. 1.改表法 可能是你 ...
- springboot logback 集成
在 application.yml 中敲 logging.pattern.consle ,IDEA 会联想到对应的值.单击属性就可以跳到 LoggingApplicationListener.java ...
- Docker - Docker中搭建MySQL主从
1.pull完centos7纯净版的镜像后,创建容器,然后将宿主机上下载的MySQL文件 (MySQL下载地址:http://mysql.mirror.kangaroot.net/Downloads/ ...
- kubernetes-PetSet
什么是Pet?Pet是一个有状态应用程序,本质上它是一个具有确定性名称以及唯一身份的Pod,身份内容包括: DNS中可以识别的固定hostname 顺序化索引(Pet名称组成:PetSetName-O ...
- ERP项目实施记录02
今天去第三方公司(B公司)考察: 公司成立:2011年12月 注册地:深圳 深圳:2~3个业务员 东莞:5个开发人员,据说也是实施人员 全功能者:BOSS A公司因战略调整,要将业务"下放& ...
- Kernel parameter requirements ( Linux DB2)
Kernel parameter requirements ( Linux DB2) https://www.ibm.com/support/knowledgecenter/SSEPGG_9.7.0/ ...