深入理解java内存模型

java内存模型的抽象

java线程之间的通信由java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见

从抽象的角度来看,JMM决定了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memeory)中,每个线程都有一个私有的本地内存(local memeory),本地内存中存储了该线程以读/写共享变量的副本.本地内存是JMM的一个抽象概念,并不真实存在.

假设: 线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

  1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去
  2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量

重排序

  1. 编译器优化的重排序。编译器在不改变单线程予以的前提下,可以重新按排序语句的执行顺序。
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  3. 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

为了保证内存可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令来进制特定类型的处理器重排序。JMM把内存屏障指令分为下列四类:

屏障类型 指令事例 说明
LoadLoad Barriesrs Load1;LoadLoad;Load2 确保Load1数据的装载,之前于Load2及所有后续装载指令的装载
StoreStore Barriers Store1;StoreStore;Store2 确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的储存
LoadStore Barriesrs Load1;LoadStore;Store2 确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存
StoreLoad Barriers Store1;StoreLoad;Load2 确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

StoreLoad Barriers同事具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一致被所有处理器支持)。

happens-before

从JDK5开始,java使用新的JSR-133内存模型。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之内。

规则如下:

  • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
  • 监视器锁规则:对一个监视器的解锁,happens-before于随后对这个监视器的加锁
  • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
  • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。

注意:两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行,happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二操作之前。

Volatile

public class MyTest1 {

    volatile long vl = 0L; // 使用volatile声明64位的long型变量

    public void set(long l) {
vl = l; //单个volatile变量的写
} public void getAndIncrement() {
vl++; //复合(多个)volatile变量的读/写
} public long get() {
return vl; //单个volatile变量的读
}
}

这个程序在语义上和下面程序等价:

public class MyTest1 {

    long vl = 0L; // 声明64位的long型变量

    public synchronized void set(long l) {
vl = l;
} public void getAndIncrement() {
long temp = get();
temp+=1L;
set(temp);
} public synchronized long get() {
return vl;
}
}

锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

volatile变量自身具有下列特性:

  • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • 原子性:对任意单个volatile变量的读、写具有原子性,但类似于volatile++这种复合操作不具有原子性。

volatile读的内存语义如下:

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

锁释放和获取的内存语义

当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。

当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须要从主内存中去读取共享变量。

final

对于final域,编译器和处理器要遵守两个重排序规则:

  1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序
  2. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

深入理解java内存模型--读书笔记的更多相关文章

  1. 《Java并发编程实战》第十六章 Java内存模型 读书笔记

    Java内存模型是保障多线程安全的根基,这里不过认识型的理解总结并未深入研究. 一.什么是内存模型,为什么须要它 Java内存模型(Java Memory Model)并发相关的安全公布,同步策略的规 ...

  2. 《深入理解 Java 内存模型》读书笔记

    ![img](https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6RtPu3BNx3zps1JhSmPICRw7QgeOmxOfTbCT3RLgIo4qRpn6xL4qg ...

  3. 转《深入理解 Java 内存模型》读书笔记

    转:https://mp.weixin.qq.com/s/2hA6u4hLEPWlTPdD-XB-bg 前提 <深入理解 Java 内存模型>程晓明著,该书在以前看过一遍,现在学的东西越多 ...

  4. 《深入理解Java内存模型》读书总结(转-总结很好)

    概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括“并发.同步.主内存.本地内存.重排序.内存屏障.happens before规则. ...

  5. 深入理解 Java 内存模型(转载)

    摘要: 原创出处 http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/ 「zhisheng」欢迎转载,保留摘要,谢谢! 0. 前提 &l ...

  6. 【并发编程】一文带你读懂深入理解Java内存模型(面试必备)

    并发编程这一块内容,是高级资深工程师必备知识点,25K起如果不懂并发编程,那基本到顶.但是并发编程内容庞杂,如何系统学习?本专题将会系统讲解并发编程的所有知识点,包括但不限于: 线程通信机制,深入JM ...

  7. 深入理解java内存模型系列文章

    转载关于java内存模型的系列文章,写的非常好. 深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模 ...

  8. 【Todo】【转载】深入理解Java内存模型

    提纲挈领地说一下Java内存模型: 什么是Java内存模型 Java内存模型定义了一种多线程访问Java内存的规范.Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几 ...

  9. 深入理解Java内存模型(一)——基础(转)

    转自程晓明的"深入理解Java内存模型"的博客 http://www.infoq.com/cn/articles/java-memory-model-1 并发编程模型的分类 在并发 ...

随机推荐

  1. js避坑历险记

    代码改变世界,世界改变码农,码农改变代码! 我就是我,我就是一个码农的武林. 前方JS巨坑出没,请注意集中力! 巨坑1:js精度问题 前段时间去一家物流公司面试,做了一个js题,印象尤为深刻: var ...

  2. 深度探索c++对象模型 第一章

    1,声明与定义. //声明式如下: extern int x;   //对象式(变量式)声明 std::size_t numDigits(int number);  //函数式声明 class Wid ...

  3. [hdu-6395]Sequence 分块+矩阵快速幂

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6395 因为题目数据范围太大,又存在递推关系,用矩阵快速幂来加快递推. 每一项递推时  加的下取整的数随 ...

  4. Python 3网络爬虫开发实战》中文PDF+源代码+书籍软件包

    Python 3网络爬虫开发实战>中文PDF+源代码+书籍软件包 下载:正在上传请稍后... 本书书籍软件包为本人原创,在这个时间就是金钱的时代,有些软件下起来是很麻烦的,真的可以为你们节省很多 ...

  5. ES6_07_Symbol属性

    Symbol属性: 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境 Symbol: 概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, ...

  6. mysql-8.0.16-winx64/Linux修改root用户密码

    连接数据库等基础操作请自行解决哈,本篇是重点记录如何改密码. 一.查询用户密码: 查询用户密码命令: select host, user, authentication_string from mys ...

  7. Redis持久化深入理解

    用过Redis的都知道,Redis有两种持久化方式:RDB和AOF,他们的区别大家应该都清楚,所以今天主要想分享一下这两种持久化方式的底层原理以及实现. 如果让你手写一个持久化(架构级)的功能,你没有 ...

  8. html select 可输入 可编辑

    <HTML> <HEAD> <META http-equiv='Content-Type' content='text/html; charset=gb2312'> ...

  9. 如何用ModelSim对Xilinx ISE产生的网表进行仿真

    图: 在对设计的芯片进行测试时,经常要用到FPGA,可是里面的仿真工具却不如Modelsim那么好用,且在规模比较大时,ISE在仿真时,软件经常会报告内存限制的问题,此时一般会切换到Modelsim软 ...

  10. JAVA十大经典排序算法最强总结(含JAVA代码实现)

    十大经典排序算法最强总结(含JAVA代码实现)   最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在“桶排序”算法中对每 ...