《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before
第16章 Java内存模型
终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻。这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略等。它们的安全性都来自于JMM。本章将介绍Java内存模型的底层需求以及所提供的保证。
16.1 什么是内存模型,为什么需要它


16.1.1 平台的内存模型
在共享内存的多处理体系架构中,每个处理器都拥有自己的缓存,并且定期地与住内存进行协调。在不同的处理器架构中提供了不同级别的缓存一致性。要想确保每个处理器在任意时刻都能知道其他处理器正在进行的工作,将需要非常大的开销。在大多数时间里,这种信息是不必要的,因此处理器会适当放宽存储一致性保证,以换取性能的提升。在架构定义的内存模型中将告诉应用程序可以从内存系统中获得怎样的保证,此外还定义了一些特殊的指令(称为内存栅栏或栅栏),当需要共享数据时,这些指令就能实现额外的存储协调保证。为了使java开发人员无需关心不同架构上内存模型之间的差异,Java还提供了自己的内存模型,并且JVM通过在适当位置上插入内存栅栏来屏蔽JMM与底层平台内存模型之间的差异。
16.1.2 重排序
JMM可以使不同线程看到的操作执行顺序是不同的,从而导致在缺乏同步的情况下,要推断操作的执行顺序将变得更加复杂。各种操作延迟或者看似乱序执行的不容原因,都可以归为重排序。


同步将限制编译器,运行时和硬件对内存操作重排序的方式,从而在实施重排序时不会破坏JMM提供的可见性保证。
16.1.3 Java内存模型简介
JMM为程序中所有的操作提供了一个偏序关系,称之为Happens-before。要想保证执行操作B的线程看到操作A的结果,那么A和B之间必须满足Happens-before关系。如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意地重排序。


16.1.4 借助同步
由于Happens-Before的排序功能很强大,因此有时候可以"借助"(Piggyback)现有同步机制的可见性属性。这需要将Happens-Before的程序规则与其他某个顺序规则结合起来,从而对某个未被锁保护的变量的访问操作进行排序。这项技术对语句的顺序非常敏感,因此很容易出错。它是一项高级技术,并且只有当需要最大限度提升某些类(例如ReentrantLock)的性能时,才应该使用这项技术。
在FutureTask的保护方法AbstractQueuedSynchronizer中说明了如何使用这种“借助”技术。




16.2 发布
第3章介绍了如何安全地发布或者不正确地发布一个对象。其中介绍的各种安全技术,它们的安全性都来自于JMM提供的保证,而造成不正确发布的真正原因,就是在“发布一个共享对象”与“另一个线程访问该对象”之间缺少一种Happens-Before排序。
16.2.1 不安全的发布
如果无法确保发布共享引用的操作在另一个线程加载该共享引用之前执行,那么对新对象引用的写入操作将与对象中各个域的写入操作重排序。在这种情况下,另一个线程可能看到对象引用的最新值,但同时也将看到对象的某些或全部状态中包含的是无效值,即一个被部分构造的对象。错误的延迟初始化将导致不正确的发布,如程序16-3所示:

16.2.2 安全地发布
第3章介绍的安全发布常用模式可以确保被发布对象对于其他线程是可见的,因为它们保证发布对象的操作将在使用对象的线程开始使用该对象的引用之前执行。
16.2.3 安全初始化模式
有时候需要推迟一些高开销的对象初始化操作,并且只有当使用这些对象时才进行初始化。在程序清单16-4中通过将getResource方法声明为synchronized可以修复UnsafeLazyInitialization中的问题。由于getInstance的代码路径很短,因此如果geiInstance没有被多个线程频繁调用,那么在SafeLazyInitialization上不会存在激烈的竞争。


如程序16-5所示,通过提前初始化,避免了在每次调用SafeLazyInitialization中的getInstance时所产生的同步开销。在程序16-6的“延迟初始化占位类模式”中使用了一个专门的类来初始化Resource。JVM将推迟ResourceHolder的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Resource,因此不需要额外的同步。


16.2.4 双重检查锁
DCL,已经被广泛的废弃了,pass
16.3 初始化过程中的安全性
小结:

终于把这本书看得差不多了....,呼呼呼,真不容易,总算又完结了一件事。这本书大概现在也只搞懂了一半左右,毕竟只看了一遍,糟糕的翻译质量为本书的阅读添加了不少的难度。除此之外,这本书理论大于实践,代码实践部分并不多,书中有很多前提知识是假定你已经知道的,所以适合有一定基础的人看。以后有时间把英文版拿来看看,最关键的是要在实践中运用这些知识才行。
《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before的更多相关文章
- Java并发编程实战 读书笔记(一)
最近在看多线程经典书籍Java并发变成实战,很多概念有疑惑,虽然工作中很少用到多线程,但觉得还是自己太弱了.加油.记一些随笔.下面简单介绍一下线程. 一 线程与进程 进程与线程的解释 个人觉 ...
- Java并发编程实战 读书笔记(二)
关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...
- 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结
<Java并发编程实战>和<Java并发编程的艺术> Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...
- 《java并发编程实战》笔记
<java并发编程实战>这本书配合并发编程网中的并发系列文章一起看,效果会好很多. 并发系列的文章链接为: Java并发性和多线程介绍目录 建议: <java并发编程实战>第 ...
- java并发编程实战《二》java内存模型
Java解决可见性和有序性问题:Java内存模型 什么是 Java 内存模型? Java 内存模型是个很复杂的规范,可以从不同的视角来解读,站在我们这些程序员的视角,本质上可以理解为, Java 内存 ...
- Java多线程编程实战读书笔记(一)
多线程的基础概念本人在学习多线程的时候发现一本书——java多线程编程实战指南.整理了一下书中的概念制作成了思维导图的形式.按照书中的章节整理,并添加一些个人的理解.
- Java并发编程实战 第16章 Java内存模型
什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...
- Java并发编程艺术读书笔记
1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...
- Java并发编程实践读书笔记(1)线程安全性和对象的共享
2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...
- Java并发编程的艺术(四)——JMM、重排序、happens-before
什么是JMM JMM就是Java内存模型.目的是为了屏蔽系统和硬件的差异,让同一代码在不同平台下能够达到相同的访问结果.规定了线程和内存之间的关系. 内存划分 JMM规定了内存主要划分为主内存和工作内 ...
随机推荐
- 多线程中Local Store Slot(本地存储槽)[转]
1. 使用ThreadStatic特性 ThreadStatic特性是最简单的TLS使用,且只支持静态字段,只需要在字段上标记这个特性就可以了: [ThreadStatic] static str ...
- maven根据不同的运行环境,打包不同的配置文件(转载)
使用maven管理项目中的依赖,非常的方便.同时利用maven内置的各种插件,在命令行模式下完成打包.部署等操作,可方便后期的持续集成使用. 但是每一个maven工程(比如web项目),开发人员在开发 ...
- java 在centos6.5+eclipse环境下调用opencv实现sift算法
java 在centos6.5+eclipse环境下调用opencv实现sift算法,代码如下: import org.opencv.core.Core; import org.opencv.core ...
- 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)
P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...
- VC使用sqlite
SQLite可以到官方站点(http://www.sqlite.org/download.html)下载:Linux,Mac OS X, Windows下的已编译文件以及源代码.帮助文档. SQLit ...
- JS 中类型鉴别
JS中的基本类型有:数字(Number(NaN,Infinity)),字符串(String),Undefined,Null,Boolean 引用类型有:数组(Array),对象(Object),函数( ...
- JS常见的算法
原文链接:Jack Pu's Blog 虽说我们很多时候前端很少有机会接触到算法.实际上学习数据结构与算法对于工程师去理解和分析问题都是有帮助的.如果将来当我们面对较为复杂的问题,这些基础知识的积累可 ...
- ES6数组的扩展运算符
一.基本使用 ES6中函数可以使用 rest参数 接收函数的多余参数,组成一个数组,放在形参的最后面. let fn = (a, ...value) => { console.log(a); c ...
- String作为输出型参数时获取不到值
有时候在一个方法中,我们需要返回多个字符串,而又不想将这些字段包成一个类.此时就需要使用输出型参数. 但是如果将输出型参数的类型声明为String,那么调用该方法后,是获取不到我们想要的值的. 测试代 ...
- 【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和
题意:给出N个数,M个操作.操作有修改和询问两种,每次修改将一个数改成另一个数,每次询问一个区间的所有连续子区间的异或和.n,m<=100000,ai<=1000 题解: 当年(其实也就是 ...