JMM和happens-before原则

vJMM:

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

v可见性:

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

v原子性:

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

v有序性:

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

v重排序:

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

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

 

vjava中volatile关键字的含义

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

vJava内存模型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. 
----------------------------

v什么是happens-before?

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

v为什么存在可见性问题?

  简单介绍下。相对于内存,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中单例的延迟加载可以用另一种方案实现(方案四): 

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

public class Singleton {

  private Singleton(){}

  private static final Singleton instance = new Singleton();

  public static Singleton getInstance() {
    return instance;   
  }
}

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

public class Singleton { 

  private static Singleton instance = null;

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

} 

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

  目的是避开过多的同步, 
但在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;   
  } 

} 

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

public class Singleton { 

  private static class Holder {
      static final Singleton instance = new Singleton();
  }

  public static Singleton getInstance() {
    return Holder.instance;   
  } 

} 

 

时间: 2024-08-30 15:32:38

JMM和happens-before原则的相关文章

Java并发编程系列之五:happens-before原则

前言 happens-before是JMM的核心,之所以设计happens-before,主要出于以下两个方面的因素考虑的:1)程序员的角度,JMM内存模型需要易于理解.易于编程:2)编译器和处理器的角度,编译器和处理器希望内存模型对其束缚越少越好,这样就可以根据自己的处理规则进行优化.但是这两个方面其实是相互矛盾的,因为JMM易于编程和理解就意味着对编译器和处理器的束缚就越多. happens-before定义 基于上面的考虑,设计JMM时采用了一种折中的选择--JMM将需要禁止的重排序分为两

深入RESTful无状态原则

前言 在上篇RESTful基础知识中整体的介绍了RESTful架构设计思想的框架,在往后的RESTful主题博文中,我们在这个框架的基础上不断的为其填充更加深入的知识材料.  RESTful基础知识,传送门:http://blog.csdn.net/jmilk/article/details/50452595 无状态原则 Statelessness:无状态原则是RESTful架构设计中一个非常重要的原则,无状态是相对于有状态而言的.在理解什么是无状态的交互请求之前,首先我们需要了解什么是有状态,

交互设计中的5项视觉指导原则

  视觉是人的第一感观,它在影响用户行为方面起着决定性作用,视觉信息对产品的交互产生了深远影响. 来自UXPin的Jerry Cao,通过本文向我们解释了如何保持视觉与交互的协调. 我不想贬低文字的重要性,但也不想忽视视觉.两者是同等重要的交互设计元素.文字就是交互,但那些视觉元素(比如图标.菜单.图像等)才是用户实际上操作的东西.虽然有些可用性专家会提及Craigslist甚至Amazon,作为丑陋但可用(而且受欢迎)的网站案例.但毫无疑问,美感总会有所帮助. 情感是用户体验的关键:视觉设计优

网页动画的十二原则

  作为前端的设计师和工程师,我们用 CSS 去做样式.定位并创建出好看的网站.我们经常用 CSS 去添加页面的运动过渡效果甚至动画,但我们经常做的东西不会超过这些. 动效是一个有助于访客和消费者理解我们设计的强有力工具.这里有些原则能最大限度地应用在我们的工作中. 迪士尼经过基础工作练习的长时间累积,在 1981 年出版的 The Illusion of Life: Disney Animation 一书中发表了动画的十二个原则 (12 Principles of Animation) .这些

优秀Web设计10项原则:创新实用富有美感

他能够为我们使用家用电器的方式掀起一场革命,现在我们仍然把它作为现代世界的设计灵感,最好例子就是Apple.Apple的许多畅销产品的核心功能都采用了Dieter Rams的设计原则. Dieter Rams最著名的设计思想是优秀设计的10项原则,这些原则通常用来做出好的设计或对其进行归类.这些原则包含了在创建项目的时候设计师需要考虑到的问题,但是这些原则最适用于工业设计,因为Dieter Rams正是在这个领域做出了非同寻常的作品,并得出了这些灵感. 这些原则并不是古板的戒律,并不是不能变通的

Android界面与交互设计原则:以用户为中心

译者按: 在iOS HIG已经强大经典了N年之后,Android终于推出了一套比较系统的HIG(大概是为了配合Android 4.0 Ice Cream Sandwich).仔细比较两套HIG的"设计原则"部分,发现完全是截然不同的两种风格.iOS HIG走的是更专业型的路线,描述严谨且有不少的专业词汇(比如Metaphors.Consistency之类的).而Android则显得亲民许多,不仅描述方式简要易懂,配图鲜明直观,甚至还用了"me"作为了一系列要点的标题

微服务的4大设计原则和19个解决方案

作者|郝炎峰 编辑|小智 本文将介绍微服务架构的演进.优缺点和微服务应用的设计原则,然后着重介绍作为一个"微服务应用平台"需要提供哪些能力.解决哪些问题才能更好的支撑企业应用架构. 注:本文转载自公众号 EAWorld,已获授权. 写在前面 微服务架构现在是谈到企业应用架构时必聊的话题,微服务之所以火热也是因为相对之前的应用开发方式有很多优点,如更灵活.更能适应现在需求快速变更的大环境. 微服务平台也是我目前正在参与的,还在研发过程中的平台产品,平台是以 SpringCloud 为基础

微服务的4个设计原则和19个解决方案

微服务架构现在是谈到企业应用架构时必聊的话题,微服务之所以火热也是因为相对之前的应用开发方式有很多优点,如更灵活.更能适应现在需求快速变更的大环境. 本文将介绍微服务架构的演进.优缺点和微服务应用的设计原则,然后着重介绍作为一个"微服务应用平台"需要提供哪些能力.解决哪些问题才能更好的支撑企业应用架构. 微服务平台也是我目前正在参与的,还在研发过程中的平台产品,平台是以SpringCloud为基础,结合了普元多年来对企业应用的理解和产品的设计经验,逐步孵化的一个微服务应用平台. 一.微

Web开发者不可不知的15条编码原则

 HTML已经走过了近20的发展历程.从HTML4到XHTML,再到最近十分火热的HTML5,它几乎见证了整个互联网的发展.但是,即便到现在,有很多基础的概念和原则依然需要开发者高度注意.下面,向大家介绍这些应该遵循的开发原则. 1.善用DIV来布局 当开发一个Web页面时,要考虑第一件事就是区分页面重点.将这些内容用DIV标签包含起来,页面的代码会呈现出整洁.缩进良好的风格. <div id="header"></div> <div id="b