JMM和happens-before原则

JMM:

  Java Memory Model(Java内存模型),围绕着在并发过程中如何处理可见性原子性有序性这三个特性而建立的模型。

可见性:

  JMM提供了volatile变量定义、final、synchronized块来保证可见性。
  例如:线程a在将共享变量x=1写入主内存的时候,如何保证线程b读取共享变量x的值为1,这就是JMM做的事情。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

原子性:

  JMM提供保证了访问基本数据类型的原子性(其实在写一个工作内存变量到主内存是分主要两步:store、write),但是实际业务处理场景往往是需要更大的范围的原子性保证,所以模型也提供了synchronized块来保证。

有序性:

  这个概念是相对而言的,如果在本线程内,所有的操作都是有序的,如果在一个线程观察另一个线程,所有的操作都是无序的,前句是“线程内表现为串行行为”,后句是“指令的重排序”和“工作内存和主内存同步延迟”现象,模型提供了volatile和synchronized来保证线程之间操作的有序性

重排序:

  在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序(编译器、处理器),就是因为这些重排序,所以可能会导致多线程程序出现内存可见性问题(数据安全问题)和有序性问题。
  JMM是如何处理的呢?
  对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序
  对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序
  总之一句话,JMM是通过禁止特定类型的编译器重排序和处理器重排序来为程序员提供一致的内存可见性保证。

  A线程具体什么时候刷新共享数据到主内存是不确定的,假设我们使用了同步原语(synchronized,volatile和final),那么刷新的时间是确定的,例如:线程A释放锁后会同步到主内存,线程B获取锁后会同步主内存数据,即“A线程释放锁--B线程获取锁”可以实现A,B线程之间的通信。

 

java中volatile关键字的含义

  http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html

Java内存模型happens-before法则

  The rules for happens-before are:
  Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order.
  Monitor lock rule. An unlock on a monitor lock happens-before every subsequent lock on that same monitor lock.
  Volatile variable rule. A write to a volatile field happens-before every subsequent read of that same field.
  Thread start rule. A call to Thread.start on a thread happens-before every action in the started thread.
  Thread termination rule. Any action in a thread happens-before any other thread detects that thread has terminated, either by successfully return from   Thread.join or by Thread.isAlive returning false.
  Interruption rule. A thread calling interrupt on another thread happens-before the interrupted thread detects the interrupt (either by having   InterruptedException tHRown, or invoking isInterrupted or interrupted).
  Finalizer rule. The end of a constructor for an object happens-before the start of the finalizer for that object.
  Transitivity. If A happens-before B, and B happens-before C, then A happens-before C.
  ----------------------------

什么是happens-before?

  happens-before就是“什么什么一定在什么什么之前运行”,也就是保证顺序性。
  因为CPU是可以不按我们写代码的顺序执行内存的存取过程的,也就是指令会乱序或并行运行,
  只有上面的happens-before所规定的情况下,才保证顺序性。

为什么存在可见性问题?

  简单介绍下。相对于内存,CPU的速度是极高的,如果CPU需要存取数据时都直接与内存打交道,在存取过程中,CPU将一直空闲,这是一种极大的浪费,妈妈说,浪费是不好的,所以,现代的CPU里都有很多寄存器,多级cache,他们比内存的存取速度高多了。某个线程执行时,内存中的一份数据,会存在于该线程的工作存储中(working memory,是cache和寄存器的一个抽象,这个解释源于《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有不少人觉得working memory是内存的某个部分,这可能是有些译作将working memory译为工作内存的缘故,为避免混淆,这里称其为工作存储,每个线程都有自己的工作存储),并在某个特定时候回写到内存。单线程时,这没有问题,如果是多线程要同时访问同一个变量呢?内存中一个变量会存在于多个工作存储中,线程1修改了变量a的值什么时候对线程2可见?此外,编译器或运行时为了效率可以在允许的时候对指令进行重排序,重排序后的执行顺序就与代码不一致了,这样线程2读取某个变量的时候线程1可能还没有进行写入操作呢,虽然代码顺序上写操作是在前面的。这就是可见性问题的由来。

  并且,多个CPU之间的缓存也不保证实时同步, 
  也就是说你刚给一个变量赋值,另一个线程立即获取它的值,可能拿到的却是旧值(或null), 
  因为两个线程在不同的CPU执行,它们看到的缓存值不一样, 
  只有在synchronized或volatile或final的性况下才能保证正确性, 
  很多人用synchronized时只记得有lock的功能,而忘记了线程间的可见性问题。 

public class Test {private int n;public void set(int n) {this.n = n;}public void check() {if (n != n)throw new Exception("check Error!");}
}

  check()中的 n != n 好像永远不会成立,因为他们指向同一个值,但非同步时却很有可能发生。 

  另外,JMM不保证创建过程的原子性,读写并发时,可能看到不完整的对象, 
  这也是为什么单例模式中著名的"双重检查成例"方法,在Java中行不通。(但.Net的内存模型保证这一点) 
  当然,在Java中单例的延迟加载可以用另一种方案实现(方案四): 

方案一:非延迟加载单例类 

public class Singleton {private Singleton(){}private static final Singleton instance = new Singleton();public static Singleton getInstance() {return instance;   } 
}

方案二:简单的同步延迟加载 

public class Singleton { private static Singleton instance = null;public static synchronized Singleton getInstance() {if (instance == null)instance = new Singleton();return instance;   } } 

方案三:双重检查成例延迟加载 

  目的是避开过多的同步, 
  但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。 
  JDK5.0以后版本若instance为volatile则可行 

  用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值。volatile很容易被误用,用来进行原子性操作。

public class Singleton { private static Singleton instance = null;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;   } } 

方案四:类加载器延迟加载 

public class Singleton { private static class Holder {static final Singleton instance = new Singleton();}public static Singleton getInstance() {return Holder.instance;   } } 

 

转载于:https://www.cnblogs.com/hujunzheng/p/5118256.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531350.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SD卡移植FAT32文件系统无MBR

问题:在研究SD卡和FAT32文件系统的时候,发现SD卡有的有MBR,有的没有MBR,这个为什么呢? 分析:MBR是主引导记录,是在给磁盘分区的时候建立的,我们的SD卡没有这个可能就是没有进行过分区…

java获取类的信息

关键技术剖析 1.java.lang.reflect包实现了java的反射机制,在使用反射机制时,需要导入该包。 2.Class类的forName方法能够根据类名加载类,获得类的Class对象。 Class类的getSuperclass方法获得父类的Class对象;getDeclaredFields方…

FAT32文件系统介绍

FAT32文件系统(一)为什么要有文件系统(二)FAT32文件系统组成(三)分步介绍各部分(1) 首先介绍一下MBR(2)DBR介绍(3)FAT表介绍(4) 数据区(一)为什么要有文件系统 文件系统是操作系统用…

java中动态代理实现机制

前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类…

libiconv库简单裁剪支持CP437编码

有许多人在做项目的时候都会遇到字符编码的不一致导致的乱码问题,那如何去解决呢?在Linux系统上可以通过iconv函数族来进行编码转换,但有时候我们并不需要全部的字符集,因为可能会占用比较大的空间,本文主要支持CP437编…

简单java在线测评程序

简单java程序在线测评程序 一.前言 大家过年好!今年的第一篇博客啊!家里没有网,到处蹭无线!日子过得真纠结!因为毕设的需求,简单写了一个java程序在线测评程序,当然也可以在本地测试…

指针强制类型转换触发内存自动对齐

C语言的指针是我们操作很灵活,但是也留下了不少坑,今天工作遇到了一个指针的坑,这里复现一下: 指针类型强制转换,并进行解引用,引起的内存对齐问题.(一)问题复现: 运行环境:Ubuntu …

简单文本编辑器

一、前言 聚天地之灵气,集日月之精华!一个简单的java文本编辑器由此而生。毕设所需,很是无奈! 二、界面预览 三、实现思路 1.字体选择器的实现 (1).字体类 class MyFont{private Font font;private Color color;public Font getFo…

u-boot新增命令后出现data abort

(一)问题描述 u-boot下新增了一条update的命令,直接输入update没有报错,但是输入up按TAB键补全时发现出现data abort,而且输入不支持的命令也会有data abort (二)解决方法 最开始我包含的头…

sublime text学习

Ctrl / ---------------------注释 Ctrl 滚动 --------------字体变大/缩小 Ctrl N-------------------新建 软件右下角可以选择文档语法模式 Ctrl Shift P ------------------命令模式 命令: sshtml模糊匹配-----语法切换到html模式,同理所得&am…

core文件如何分析

目录(一)什么是coredump(二)coredump产生的条件(1)coredump产生主要原因:(2)如何生成coredump(三)gdb使用(四)实例调试coredump文件(五)总结(一)什么是coredu…

SpringMVC+FreeMarker

前言: 最近在学习SpringMVC,模板引擎用的是FreeMarker,之前没有接触过。利用SpringMVC开发其实还有许多的步骤,比如控制层,服务层,持久化层,实体等等,先弄了一个小demo来总结一下Spr…

SpringMVC那点事

一、SpringMVC返回json数据的三种方式 1、第一种方式是spring2时代的产物&#xff0c;也就是每个json视图controller配置一个Jsoniew。 如&#xff1a;<bean id"defaultJsonView" class"org.springframework.web.servlet.view.json.MappingJacksonJsonView&q…

js学习内容的整理

1、jquery动态添加Table中的一行 function addTableRow(tableId){var html <tr>\......\</tr>";//行首插入一行if($(#tableId).find(tr).length 1){$(html).insertAfter($(#tableId).find(tr).eq(0));} else { $(html).insertBefore($(#tableId).find(tr).e…

(一)最邻近插值python实现

这里写目录标题&#xff08;一&#xff09;原始图像&#xff08;二&#xff09;最邻近插值实现&#xff08;三&#xff09;python实现1. 安装库2. python程序编写3. 效果4. 工程文件&#xff08;一&#xff09;原始图像 &#xff08;二&#xff09;最邻近插值实现 一般情况下我…

(二)双线性插值python实现

这里写目录标题&#xff08;一&#xff09;原始图像&#xff08;二&#xff09;双线性插值原理&#xff08;三&#xff09;python实现1. 安装库2. python程序编写3. 效果4. 工程文件&#xff08;一&#xff09;原始图像 &#xff08;二&#xff09;双线性插值原理 一般情况下我…

js self = this的解释

Demo 1: function Person(){this.name hjzgg;this.age 24;this.show function(){alert(name " " age);}}var p new Person();p.show(); 错误&#xff1a;name 和 age都没有定义。 Demo 2: function Person(){this.name hjzgg;this.age 24;this.show functio…

(三)图像转灰度图Python实现

这里写目录标题&#xff08;一&#xff09;原始图像&#xff08;二&#xff09;转换原理&#xff08;三&#xff09;python实现1. 安装库2. python程序编写3. 效果4. 工程文件&#xff08;一&#xff09;原始图像 &#xff08;二&#xff09;转换原理 &#xff08;三&#xff…

SD卡实例分析fat32文件系统

目录 环境描述 分析过程 1.SD卡格式化 2.使用winhex打开sd卡 3.MBR分析 4.DBR分析

java泛型上下限

前言&#xff1a; java的泛型上下限不是很好理解&#xff0c;尤其像我这种菜鸡。反反复复看了好几遍了...&#xff0c;真是... 一、简单的继承体系 class Person{}class Student extends Person{}class Worker extends Person{} 二、泛型上限&#xff08;extends 关键字&#x…