`
javatar
  • 浏览: 1701118 次
  • 性别: Icon_minigender_1
  • 来自: 杭州699号
社区版块
存档分类
最新评论

Java内存模型happens-before法则

阅读更多
JMM规范:

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所规定的情况下,才保证顺序性。
如:
public class Test {

	private int a = 0;

	private long b = 0;

	public void set() {
		a = 1;
		b = -1;
	}

	public void check() {
		if (! ((b == 0) || (b == -1 && a == 1))
			throw new Exception("check Error!");
	}
}

对于set()方法的执行:
1. 编译器可以重新安排语句的执行顺序,这样b就可以在a之前赋值。如果方法是内嵌的(inline),编译器还可以把其它语句重新排序。
2. 处理器可以改变这些语句的机器指令的执行顺序,甚到同时执行这些语句。
3. 存储系统(由于被缓存控制单元控制)也可以重新安排对应存储单元的写操作顺序,这些写操作可能与其他计算和存储操作同时发生。
4. 编译器,处理器和存储系统都可以把这两条语句的机器指令交叉执行。
例如:在一台32位的机器上,可以先写b的高位,然后写a,最后写b的低位,(注:b为long类型,在32位的机器上分高低位存储)
5. 编译器,处理器和存储系统都可以使对应于变量的存储单元一直保留着原来的值,
以某种方式维护相应的值(例如,在CPU的寄存器中)以保证代码正常运行,直到下一个check调用才更新。
...
在单线程(或同步)的情况下,上面的check()永远不会报错,
但非同步多线程运行时却很有可能。


并且,多个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则可行
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;   
  } 

} 

分享到:
评论
4 楼 renyisheng 2014-09-09  
http://www.infoq.com/cn/articles/java-memory-model-2
如果A happens- before B,JMM并不要求A一定要在B之前执行。JMM仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。这里操作A的执行结果不需要对操作B可见;而且重排序操作A和操作B后的执行结果,与操作A和操作B按happens- before顺序执行的结果一致。在这种情况下,JMM会认为这种重排序并不非法(not illegal),JMM允许这种重排序。
在计算机中,软件技术和硬件技术有一个共同的目标:在不改变程序执行结果的前提下,尽可能的开发并行度。编译器和处理器遵从这一目标,从happens- before的定义我们可以看出,JMM同样遵从这一目标。
3 楼 mercyblitz 2010-03-15  
happens-before 保证同步主内存,而不是线程内存站(寄存器)。

happens-before 是一种不强的线程安全保证。
2 楼 yuyee 2010-01-04  
的确是好文
1 楼 pandonix 2009-03-10  
好文,难得看到深入研究JMM的

相关推荐

    浅谈Java内存模型之happens-before

    "浅谈Java内存模型之happens-before" Java内存模型是Java虚拟机中的一种机制,用于定义Java程序中线程之间的内存访问方式。在多线程环境下,线程之间的通信和数据共享是非常复杂的,需要有一些规则来保证数据的一致...

    java内存模型详解--非常经典

    此外,Java内存模型还定义了happens-before原则,这是一种逻辑上的顺序关系,用于指导编译器和JVM如何保证内存操作的正确顺序,从而保证并发程序的正确性。例如,一个线程中的构造函数完成happens-before这个线程...

    happens-before俗解

    除了上述两个核心规则之外,Java内存模型还定义了一些其他的happens-before规则,包括但不限于: 1. **线程启动-结束规则**:“线程的启动操作happens-before该线程的任何动作。”这意味着当一个新线程被启动时,...

    Java内存之happens-before和重排序

    Java内存模型中的happens-before和重排序 Java内存模型(JMM)是Java虚拟机(JVM)中的一种抽象模型,用于描述Java程序中的内存交互。其中,happens-before关系是JMM中的一种基本概念,用于描述操作之间的顺序关系...

    Java内存模型(JMM)及happens-before原理

    Java内存模型(JMM)及happens-before原理 Java内存模型(JMM)是Java虚拟机(JVM)中的一组规则,用于定义 Java 程序中的内存访问顺序。它规定了在多线程环境中,变量的写入操作对其他线程的可见性。JMM的主要目的是...

    java 内存模型 jsr-133

    JMM定义了happens-before原则来确保正确的操作顺序。 #### 最终字段(Final Fields) 最终字段是Java中的一个重要概念,它们的值一旦被初始化就不能改变。在JMM中,对于最终字段的行为有特殊的考虑。文档中特别...

    13Java内存模型1

    happens-before原则是Java内存模型中的一个重要概念,它定义了两个操作之间的内存可见性。如果操作X happens-before 操作Y,那么X对Y来说是可见的,即Y能看到X的结果。这意味着在多线程环境下,X的执行不会被Y看到的...

    Java内存模型的历史变迁.docx

    Java内存模型是Java多线程编程中至关重要的概念,它定义了线程如何访问和修改共享数据,确保并发执行的正确性。Java内存模型的历史变迁主要经历了从旧内存模型到新内存模型的演变,这一转变是为了更好地处理并发场景...

    Java 内存模型

    “Happens-Before一致性”为程序提供了一种部分排序规则,规定了一系列事件的前后顺序;“因果性”确保了内存操作和线程行为之间建立了一种因果关联。 Java内存模型的正式规范还包括了对行为和执行的定义,以及对...

    深入浅出了解happens-before原则

    在 Java 内存模型(JMM)中,happens-before 原则是一个核心概念,它规定了多线程环境下的可见性原则。该原则的核心思想是:两个操作之间的 happens-before 关系,并不意味着前一个操作必须在后一个操作之前执行。...

    深入理解Java内存模型 pdf 超清版

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了程序中各个线程如何访问和修改共享变量,以及如何确保数据的一致性。深入理解Java内存模型对于编写高效的并发程序至关重要。本文...

    深入理解 Java 内存模型_程晓明_InfoQ_java_内存模型_

    Java内存模型,简称JMM(Java Memory Model),是Java编程语言规范的一部分,它定义了线程如何共享和访问内存,以及在多线程环境中如何保证数据一致性。理解JMM对于编写高效、正确且线程安全的Java代码至关重要。 ...

    深入理解Java内存模型

    Java内存模型是并发编程中一个至关重要的概念,它定义了共享变量的访问规则,以及这些变量如何在多线程环境下进行读写操作。在深入理解Java内存模型之前,我们需要先了解并发编程模型的分类,然后掌握Java内存模型的...

    Java内存模型的历史变迁

    ### Java内存模型的历史变迁 #### 一、引言 随着多核处理器的普及与高性能计算需求的增长,Java作为主流编程语言之一,对于并发处理的支持变得越来越重要。Java内存模型(Java Memory Model,简称JMM)作为Java...

    深入Java内存模型-JMM

    Java内存模型,简称JMM(Java Memory Model),是Java虚拟机规范中定义的一个抽象概念,它描述了在多线程环境下,如何保证各个线程对共享数据的一致性视图。JMM的主要目标是定义程序中各个变量的访问规则,以及在...

    深入理解Java内存模型.程晓明(带书签文字版).pdf

    happens-before 10 重排序 13 数据依赖性 13 as-if-serial 语义 13 程序顺序规则 15 重排序对多线程的影响 15 顺序一致性 19 数据竞争与顺序一致性保证 19 顺序一致性内存模型 19 同步程序的顺序一致性效果 ...

    JSR133中文版.pdf

    happens-before原则是Java内存模型中的一个概念,定义了操作间的顺序关系,以保证内存操作的可预测性。final字段的语义关乎不可变对象的线程安全性和对final字段的初始化保证。 文档中还提到了Java内存模型的近似...

Global site tag (gtag.js) - Google Analytics