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内存模型的更多相关文章

  1. 《java并发编程实战》读书笔记13--Java内存模型,重排序,Happens-Before

    第16章 Java内存模型 终于看到这本书的最后一章了,嘿嘿,以后把这本书的英文版再翻翻.这本书中尽可能回避了java内存模型(JMM)的底层细节,而将重点放在一些高层设计问题,例如安全发布,同步策略 ...

  2. 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结

    <Java并发编程实战>和<Java并发编程的艺术>           Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...

  3. java并发编程实战《二》java内存模型

    Java解决可见性和有序性问题:Java内存模型 什么是 Java 内存模型? Java 内存模型是个很复杂的规范,可以从不同的视角来解读,站在我们这些程序员的视角,本质上可以理解为, Java 内存 ...

  4. Java并发编程实战 第16章 Java内存模型

    什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...

  5. Java并发编程(十四)Java内存模型

    1.共享内存和消息传递 线程之间的通信机制有两种:共享内存和消息传递:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程 ...

  6. 《Java并发编程实战》文摘

    更新时间:2017-06-03 <Java并发编程实战>文摘,有兴趣的朋友可以买本纸质书仔细研究下. 一 线程安全性 1.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何 ...

  7. Java并发编程实战——读后感

    未完待续. 阅读帮助 本文运用<如何阅读一本书>的学习方法进行学习. P15 表示对于书的第15页. Java并发编程实战简称为并发书或者该书之类的. 熟能生巧,不断地去理解,就像欣赏一部 ...

  8. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

  9. 【Java并发编程实战】-----“J.U.C”:Semaphore

    信号量Semaphore是一个控制访问多个共享资源的计数器,它本质上是一个"共享锁". Java并发提供了两种加锁模式:共享锁和独占锁.前面LZ介绍的ReentrantLock就是 ...

  10. 《Java并发编程实战》/童云兰译【PDF】下载

    <Java并发编程实战>/童云兰译[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062521 内容简介 本书深入浅出地介绍了Jav ...

随机推荐

  1. C++ 智能指针六

    /* 智能指针unique_ptr */ #include <iostream> #include <string> #include <memory> #incl ...

  2. android 编程之 PopupWindow 窗口的弹出

    PopupWindow 是一个可以显示在当前 Activity 之上的浮动容器,PopupWindow 弹出的位置是能够改变的,按照有无偏移量,可以分为无偏移和有偏移两种:按照参照对象的不同又可以分为 ...

  3. python-docx 设置标题heading的中文字体类型+设置正文的中文字体类型

    依赖包: from docx import Document from docx.shared import Pt from docx.shared import Inches from docx.o ...

  4. scala 模式匹配详解 2 scala里是怎么实现的?

    在这篇martin和另外两位模式匹配领域专家的论文里说了模式匹配的几种实现方式,以及scala是选择哪种方式来实现的.http://lampwww.epfl.ch/~emir/written/Matc ...

  5. I - 迷宫问题

    定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, ...

  6. include_once与require_once的区别

    ①作用及用法  可以减少代码的重复 include(_once)("文件的路径")与require(_once)("文件的路径") ②理解 说白了,就是用包含进 ...

  7. 在windows下搭建vueJS开发环境

    转自:https://www.cnblogs.com/RexSheng/p/6934413.html nodejs官网http://nodejs.cn/下载安装包,无特殊要求可本地傻瓜式安装,这里选择 ...

  8. Inotify+rsync实现实时数据同步

    使用rsync可以实现数据同步,但是即使使用crontab定时任务最小执行间隔为1分钟,在数据实时性要求比较高场合需使用inotify+rsync实现实时同步 下载inotify wget https ...

  9. Jenkins设置备份

    安装备份插件,系统管理-插件管理 可选插件搜索backup 备份 系统管理-Backup manager 设置备份路径 恢复

  10. 标准库random

    pseudo-random number generators for various distributions. Almost all module functions depend on the ...