深入理解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. yii中find()指定条件

    find()查找指定的条件 $modelName::model->find(); 使用条件对象 $criteria = new CDbCriteria(); $criteria->sele ...

  2. eclipse下jdbc数据源与连接池的配置及功能简介

    今天在做四则运算网页版的时候遇到了一个困惑,由于需要把每个产生的式子存进 数据库,所以就需要很多次重复的加载驱动,建立连接等操作,这样一方面写程序不方便,加大了程序量,另一方面,还有导致数据库的性能急 ...

  3. 授权公钥登录,sudo权限脚本

    #!/bin/bash############################################################### File Name: key_auth.sh# V ...

  4. 用户点击获取验证码之后我们会发送一条信息到用户手机,然后就会出现一个倒计时按钮,很像支付宝手机付款效果了,下面我给大家分享两个js效果

    js代码  代码如下 复制代码 <div class="input">    <input type="button" id="bt ...

  5. elasticsearch 集群部署,版本 5.5.0

    准备说明: 两台服务器 Ip分别为 192.168.239.78(主),192.168.239.49(从) 主服务器上配置如下:1.上传es5.5.0版本至主服务器2.解压 unzipunzip el ...

  6. 灵雀云CTO陈恺应邀出席国泰君安信息产业投资峰会,探讨全球科技产业新格局

    2019年7月9-10日,国泰君安信息产业投资峰会在上海陆家嘴举办.作为国内容器PaaS领域的龙头公司,灵雀云受邀出席本次大会,在“数字化转型从云做起”的论坛中,CTO陈恺发表了<云原生助力企业 ...

  7. Bzoj 3124: [Sdoi2013]直径 题解

    3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status] ...

  8. NOIP2011聪明的质监员题解

    631. [NOIP2011] 聪明的质监员 ★★   输入文件:qc.in   输出文件:qc.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 小 T 是一名质量监督 ...

  9. ASP.NET CORE 入门教程(附源码)

    ASP.NET CORE 入门教程 第一课 基本概念 基本概念 Asp.Net Core Mvc是.NET Core平台下的一种Web应用开发框架 符合Web应用特点 .NET Core跨平台解决方案 ...

  10. 数据库中 ’’ 和 NULL的区别

    null不是对象,''是对象 从'',你就可以知道这是一个字符串类型的数据,是一个长度为零的字符串. 从NULL,你只能知道这里没有赋过值,是空的,他不属于任何数据类型. 我们在数据库实际使用中,一般 ...