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.

appens-before完整规则:

(1)同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。

(2)对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。

(3)对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。

(4)Thread.start()的调用会happens-before于启动线程里面的动作。

(5)Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false。

(6)一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。

(7)一个对象构造函数的结束happens-before与该对象的finalizer的开始

(8)如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。

----------------------------

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

  1. public class Test {
  2. private int a = 0;
  3. private long b = 0;
  4. public void set() {
  5. a = 1;
  6. b = -1;
  7. }
  8. public void check() {
  9. if (! ((b == 0) || (b == -1 && a == 1))
  10. throw new Exception("check Error!");
  11. }
  12. }

对于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的功能,而忘记了线程间的可见性问题。 
如:

  1. public class Test {
  2. private int n;
  3. public void set(int n) {
  4. this.n = n;
  5. }
  6. public void check() {
  7. if (n != n)
  8. throw new Exception("check Error!");
  9. }
  10. }

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

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

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

  1. public class Singleton {
  2.   private Singleton(){}
  3.   private static final Singleton instance = new Singleton();
  4.   public static Singleton getInstance() {
  5.     return instance;   
  6.   }
  7. }

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

  1. public class Singleton {
  2.   private static Singleton instance = null;
  3.   public static synchronized Singleton getInstance() {
  4.     if (instance == null)
  5.       instance = new Singleton();
  6.     return instance;   
  7.   }
  8. }

方案三:双重检查成例延迟加载 
目的是避开过多的同步, 
但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。 
JDK5.0以后版本若instance为volatile则可行

  1. public class Singleton {
  2.   private static Singleton instance = null;
  3.   public static Singleton getInstance() {
  4.     if (instance == null) {
  5.         synchronized (Singleton.class) {
  6.             if (instance == null) {
  7.                 instance = new Singleton();
  8.             }
  9.         }
  10.     }
  11.     return instance;   
  12.   }
  13. }

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

    1. public class Singleton {
    2.   private static class Holder {
    3.     static final Singleton instance = new Singleton();
    4.   }
    5.   public static Singleton getInstance() {
    6.     return Holder.instance;   
    7.   }
    8. }

JMM规范的更多相关文章

  1. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  2. 深入理解JMM(Java内存模型) --(三)顺序一致性

    数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.Java内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量, 在另一个线程读同一个变量, 而且写和读没有通过同步来排序. 当代码 ...

  3. Java内存模型(JMM)

    JVM与线程(线程在JVM中) 1.JVM什么时候启动?         类被调用时启动,此时会启动JVM线程然后再是其他的线程(main) 2.JVM内存区域 除了程序计数器(PC)之外都有可能发生 ...

  4. Java并发指南2:深入理解Java内存模型JMM

    本文转载自互联网,侵删   一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...

  5. Java并发-Java内存模型(JMM)

    先来说说什么是内存模型吧 在硬件中,由于CPU的速度高于内存,所以对于数据读写来说会出现瓶颈,无法充分利用CPU的速度,因此在二者之间加入了一个缓冲设备,高速缓冲寄存器,通过它来实现内存与CPU的数据 ...

  6. Java内存模型(JMM)那些事

    本文是库存文章,去年年底学习了慕课网的并发编程课程,今年年初看完了<深入理解Java虚拟机>这本书,但是很多内容忘得差不多了,打算写写博客回忆一下那些忘在脑后的知识点. 温故而知新 更多J ...

  7. Java进阶专题(十一) 探究JMM

    前言 ​ JMM即java内存模型,JMM研究的就是多线程下Java代码的执行顺序,共享变量的读写.它定义了Java虚拟机在计算机内存中的工作方式.从抽象角度看,JMM定义了线程和主存之间的抽象关系: ...

  8. 深入理解Java内存模型JMM

    本文转载自深入理解Java内存模型JMM JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执 ...

  9. Java内存模型深度解析:顺序一致性--转

    原文地址:http://www.codeceo.com/article/java-memory-3.html 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据 ...

随机推荐

  1. INV 调试: 如何获取库存物料事务处理调试信息

     1. 按如下方式设置系统配置文件值: 系统配置文件值 地点/用户/应用/职责层配置文件值 --汇总 FND: 启用调试日志   是 FND:调试日志层级   陈述 INV: 调试跟踪: 是 IN ...

  2. android仿iphone的地区选择

    最近项目要做一个,类似淘宝手机客户端的,选择收货地址的三级联动滚动选择组件,下面是它的大致界面截图: 在IOS中有个叫UIPickerView的选择器,并且在dataSource中定义了UIPicke ...

  3. (七十三)iOS本地推送通知的实现

    iOS的推送通知分为本地推送和网络推送两种,如果App处于挂起状态,是可以发送本地通知的,如果已经被杀掉,则只有定时通知可以被执行,而类似于QQ的那种网络消息推送就无法实现了,因为App的网络模块在被 ...

  4. 为学Android,我看了这些书

    刚刚开始新的学习生活时,很容易走错方向,然后,这意味着不知道该学习什么,不知道该怎样学习,很显然,我写下这句话意味着我走过这样的路,为此,就付出了不小的代价,浪费了很多时间.       这篇文章当然 ...

  5. 记一个SwipeMenuListView侧滑删除错乱的Bug

    做侧滑删除网上有很多方案,比如重写Listview实现滑动的监听,今天说下一个SwipeListView,这个是之前一个朋友在网上开源的一个封装组件,能够适用于多种情况,项目地址:https://gi ...

  6. Oracle开发环境搭建

    一.软件准备 地址:oracle官网 安装包:因为个人学习用,所以就安装服务器端就可以了,不需要客户端. 一共两个压缩文件,解压时一起解压到到一个文件夹. 本人使用的:win32_11gR2_data ...

  7. MFC的两个问题

    1.启动WinApp的时候,报 ASSERT(AfxGetThread() == NULL)错误依赖的MFC DLL工程设置里面加上_USRDLL2. MFC误报内存泄露全局对象释放的问题,添加mfc ...

  8. SMEM介绍

    SMEM :shared memory,是高通平台各子系统共享信息的一种机制,通过SMEM机制,PBL可以将信息传递给SBL1,SBL1可以将信息传递给RPM.LK.下面分析一个SMEM信息传递的具体 ...

  9. 学习pthreads,给线程传递多个参数

    上篇博文中,boss线程给其他线程传递的只有一个参数,那么假如是多个参数呢?怎么传递呢?或许你会有这样的疑问,带着这个疑问,我们进入本文的世界,这里传递多个参数,采用结构体,为什么呢?因为结构体里可以 ...

  10. scrapy模拟登录微博

    http://blog.csdn.net/pipisorry/article/details/47008981 这篇文章是介绍使用scrapy模拟登录微博,并爬取微博相关内容.关于登录流程为嘛如此设置 ...