《Yangzai的知识地图》-Java内存及JSR-133我的理解

Java内存及JSR-133我的的一些理解

一、JMM的创建

JMM【Java Memory Model】内存模型 是围绕着并发场景下如何处理原子性、可见性、有序性来创建的,数组

1.一、原子性

Java内存模型来直接保证的原子性变量操做包括read、load、assign、use、store和write这六个;若是应用场景须要一个更大范围的原子性保证(常常会遇到),Java内存模型还提供了lock和 unlock操做来知足这种需求可使用synchronized【底层monitorenter + monitorexit】。多线程

1.二、可见性

volatile + final + synchronized。并发

1.三、有序性

volatile和synchronized两个关键字来保证线程之间操做的有序性,volatile关键字本 身就包含了禁止指令重排序的语义,而synchronized则是由“一个变量在同一个时刻只容许一条线程对 其进行lock操做”这条规则得到的,这个规则决定了持有同一个锁的两个同步块只能串行地进入。app

1.四、总结

synchronized 很万能能够保证原子性+有序性+可见性ide

volatile能够保证可见性+有序性【不能够保证原子性】优化

二、happens-before和synchronized-with

happens-before能够解决不跨线程的有序性,保证了时序性,可是基于Happens-before的内存模型是较弱的,会发生一些违反因果关系的结果【当一个写操做发生在了一个其依赖的读操做以前,咱们将这样的问题称为因果关 系,由于它涉及写操做是否会触发自身发生的问题。】,可是缺彻底遵循happens-before规则,因此为了解决happens-before的问题JSR-133有提出了一个synchronized-with来解决。idea

Happens-Before and Synchronizes-With Edges If we have two actions x and y, we use
x
hb → y to mean that x happens-before y. If x and y are actions of the same thread and x comes
before y in program order, then x
hb → y.复制代码

在这里插入图片描述

2.一、synchronized-with

synchronized-with是个发生在两个不一样thread间的同步行为,当A synchronized-with B的时,表明A对内存操做的效果,对于B是可见的。而A和B是两个不一样的thread的某个操做。其实synchronized-with就是跨thread版本的happens-before。所以,Java必需要定义一些特殊的语法,像volatile, synchronized, final来确保针对同一个变数的跨thread内存操做可以正确的同步。线程

2.二、synchronized关键字能够保证

Mutual Exclusive【互斥性】 对同一个对象而言,不可能有两个前缀synchronized的方法同时交错执行,当一个thread正在执行前缀synchronized的方法时,其余想执行synchronized方法的thread会被挡住。 创建Happens Before关系 对同一个对象而言,当一个thread离开synchronized方法时,会自动对接下来调用synchronized方法的thread创建一个happens-before关系,前一个synchronized的方法对该对象所作的修改,保证对接下来进入synchronized方法的thread可见。【有序性】3d

要确保这件事情,表明JVM必需要作两件事,一个是在离开synchronized代码段时,把local processor的cache写入到内存内,另外一个是在进入下一个synchronized前,要让local cache失效,使处理器从新去main memory抓正确的值。这样才可以确保每次进入synchronized代码段时,对象的状态是最新的。【内存可见性】orm

三、happens-before的顺序

在这里插入图片描述

四、synchronized-with的描述

 某个管程 m 上的解锁动做 synchronizes-with 全部后续在 m 上的锁定动做 (这里的后续是根据同步顺序定义的)。

 对 volatile 变量 v 的写操做 synchronizes-with 全部后续任意线程对 v 的读操 做(这里的后续是根据同步顺序定义的)。

 用于启动一个线程的动做 synchronizes-with 该新启动线程中的第一个动做。

 线程 T1 的最后一个动做 synchronizes-with 线程 T2 中任一用于探测 T1 是否 终止的动做。T2 可能经过调用 T1.isAlive()或者在 T1 上执行一个 join 动做 来达到这个目的。

若是线程 T1 中断了线程 T2,T1 的中断操做 synchronizes-with 任意时刻任 何其它线程(包括 T2)用于肯定 T2 是否被中断的操做。这能够经过抛出 一个 InterruptedException 或调用 Thread.interrupted 与 Thread.isInterrupted 来实现。

 为每一个变量写默认值(0,false 或 null)的动做 synchronizes-with 每一个线程 中的第一个动做。 虽然在对象分配以前就为该对象中的变量写入默认值看起来有些奇怪,从 概念上看,程序启动建立对象时都带有默认的初始值。所以,任何对象的 默认初始化操做 happens-before 程序中的任意其它动做(除了写默认值的 操做)。

 调用对象的终结方法时,会隐式的读取该对象的引用。从一个对象的构造 器末尾到该引用的读取之间存在一个 happens-before 边缘。注意,该对象 的全部冻结操做 happen-before 前面那个 happens-before 边缘 的起始点。

五、如何判断是否被正确的同步

There are two key ideas to understanding whether a program is correctly synchronized:Conflicting Accesses Two accesses (reads of or writes to) the same shared field or array element
are said to be conflicting if at least one of the accesses is a write.
Happens-Before Relationship Two actions can be ordered by a happens-before relationship.
If one action happens-before another, then the first is visible to and ordered before the second.
It should be stressed that a happens-before relationship between two actions does not imply that
those actions must occur in that order in a Java platform implementation. The happens-before
relation mostly stresses orderings between two actions that conflict with each other, and defines
when data races take place. There are a number of ways to induce a happens-before ordering,
including:
• Each action in a thread happens-before every subsequent action in that thread.
• An unlock on a monitor happens-before every subsequent lock on that monitor.
• A write to a volatile field happens-before every subsequent read of that volatile.
• A call to start() on a thread happens-before any actions in the started thread.
• All actions in a thread happen-before any other thread successfully returns from a join() on
that thread.
• If an action a happens-before an action b, and b happens before an action c, then a happensbefore c.复制代码
理解一个程序是否被正确的同步了,有两个关键概念:
冲突访问(Conflicting Accesses) 对同一个共享字段或数组元素存在两个访问(读
或写),且至少有一个访问是写操做,就称做有冲突。
Happens-Before 关系 两个动做(action)能够被 happens-before 关系排序。若是一
个动做 happens-before 另外一个动做,则第一个对第二个可见,且第一个排在第二个
以前。必须强调的是,两个动做之间存在 happens-before 关系并不意味着这些动做
在 Java 中必须以这种顺序发生。happens-before 关系主要用于强调两个有冲突的动
做之间的顺序,以及定义数据争用的发生时机。能够经过多种方式包含一个
happens-before 顺序,包括:
 某个线程中的每一个动做都 happens-before 该线程中该动做后面的动做。
 某个管程上的 unlock 动做 happens-before 同一个管程上后续的 lock 动做。
 对某个 volatile 字段的写操做 happens-before 每一个后续对该 volatile 字段的读操做。
 在某个线程对象上调用 start()方法 happens-before 该启动了的线程中的任意动做。
 某个线程中的全部动做 happens-before 任意其它线程成功从该线程对象上的
 若是某个动做 a happens-before 动做 b,且 b happens-before 动做 c,则有 a
happens-before c.复制代码

六、关联的DCL问题

public class Singleton {
	private int i = 0;
    private static Singleton instance = null;
    public static Singleton getInstance() {
        if(null == instance) {                    
            instance = new Singleton();        // 1
            i++;  //2
        }

        return instance;

    }
}复制代码

不管对于1;或2都会被即便编译器优化i++ 分为三步 取i、加1;赋值 ,1分为 new对象、初始化、关联引用

多线程状况下都会有可能被其余线程获取到初始化不彻底的对象和变量。

因此须要使用volatile进行内存可见性的保证。

七、参考资料

深刻理解Java虚拟机:JVM高级特性与最佳实践(第3版)

相关文章
相关标签/搜索