JMM与可见性

作者: 小蜗牛Aaron | 来源:发表于2020-01-24 22:03 被阅读0次

JMM jvm运行时数据区域

image.png image.png

Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。

而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。

提出JMM的原因

  1. 多线程中的问题
    1、 所见非所得
    2、 无法肉眼去检测程序的准确性
    3、 不同的运行平台有不同的表现
    4、 错误很难重现


    image.png

CPU高速缓存

image.png

CPU指令重排

image.png

Java编程语言的语义允许Java编译器和微处理器进行执行优化,这些优化导致了与其交互的代码不再同步,从而导致看似矛盾的行为

JIT编译器

脚本语言 与 编译语言的区别?
解释执行:即咱们说的脚本,在执行时,由语言的解释器将其一条条翻译成机器可识别的指令。
编译执行:将我们编写的程序,直接编译成机器可以识别的指令码。

image.png

Volitile关键字

可见性:让一个线程对共享变量的修改,能够及时的被其他线程看到 。
Java内存模型中规定:

对某个 volatile 字段的写操作 happens-before 每个后续对该 volatile 字段的读操作。
对 volatile 变量 v 的写入,与所有其他线程后续对 v 的读同步

Volatile如何实现它的语义:

禁止缓存;
volatile变量的访问控制符会加个ACC_VOLATILE
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5
对volatile变量相关的指令不做重排序;

Shared Variables定义

可以在线程之间共享的内存称为共享内存或堆内存。
所有实例字段、静态字段和数组元素都存储在堆内存中,这些字段和数组都是标题中提到的共享变量。
冲突:如果至少有一个访问是写操作,那么对同一个变量的两次访问是冲突的。
这些能被多个线程访问的共享变量是内存模型规范的对象。

对于同步规则的定义

1.对 volatile 变量 v 的写入,与所有其他线程后续对 v 的读同步
2.对于监视器 m 的解锁与所有后续操作对于 m 的加锁同步
3.对于每个属性写入默认值(0, false,null)与每个线程对其进行的操作同步启动线程的操作与线程中的第一个操作同步
4.线程 T2的最后操作与线程 T1 发现线程 T2 已经结束同步。( isAlive ,join可以判断线程是否终结)
5.如果线程 T1 中断了 T2,那么线程 T1 的中断操作与其他所有线程发现 T2 被中断了同步
6.通过抛出 InterruptedException 异常,或者调用 Thread.interrupted 或 Thread.isInterrupted

Happen-before 先行先发生原则

happens-before 关系用于描述两个有冲突的动作之间的顺序,如果一个action happends before 另一个action,则第一个操作被第二个操作可见,JVM需要实现如下happens-before规则:
1.某个线程中的每个动作都 happens-before 该线程中该动作后面的动作。
2.某个管程上的 unlock 动作 happens-before 同一个管程上后续的 lock 动作
3.对某个 volatile 字段的写操作 happens-before 每个后续对该 volatile 字段的读操作
4.在某个线程对象上调用 start()方法 happens-before 被启动线程中的任意动作
5.如果在线程t1中成功执行了t2.join(),则t2中的所有操作对t1可见
6.如果某个动作 a happens-before 动作 b,且 b happens-before 动作 c,则有 a happens-before c.

final在jmm中的处理

final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。伪代码示例:f = new finalDemo(); 读取到的 f.x 一定最新,x为final字段。

word-tearing 字节处理

有些处理器(尤其是早期的 Alphas 处理器)没有提供写单个字节的功能。在这样的处理器上更新 byte 数组,若只是简单地读取整个内容,更新对应的字节,然后将整个内容再写回内存,将是不合法的。
这个问题有时候被称为“字分裂(word tearing)”,更新单个字节有难度的处理器,就需要寻求其它方式来解决问题。
因此,编程人员需要注意,尽量不要对byte[]中的元素进行重新赋值,更不要在多线程程序中这样做。

double和long类型的特殊处理

由于《Java语言规范》的原因,对非 volatile 的 double、long 的单次写操作是分两次来进行的,每次操作其中32位,这可能导致第一次写入后,读取的值是脏数据,第二次写完成后,才能读到正确值。

读写volatile 修饰的 long、double是原子性的。
商业JVM不会存在这个问题,虽然规范没要求实现原子性,但是考虑到实际应用,大部分都实现了原子性。
《Java语言规范》中说道:建议程序员将共享的64位值(long、double)用volatile修饰或正确同步其程序以避免可能的复杂的情况

相关文章

  • JMM与可见性

    JMM jvm运行时数据区域 Java 内存区域和内存模型是不一样的东西,内存区域是指 Jvm 运行时将数据分区域...

  • JUC编程:Volatile-深入理解CAS-各种锁

    1. JMM 请你谈谈你对 Volatile 的理解 保证可见性 不保证原子性 禁止指令重排 什么是JMM JMM...

  • 浅谈java高并发

    JMM JMM是java的内存模型,JMM的关键技术就是围绕着多线程的原子性,可见性,有序性来建立的。 1.原子性...

  • JMM + volatile+CAS+ABA

    文章讲解流程JMM ==> volatile 解决可见性 ==>AtomicInteger解决原子性 ==> CA...

  • java初入多线程5

    volatile 与java内存模型(JMM) java的内存模型都是围绕着原子性、有序性、还有可见性来展开的。 ...

  • Java-可见性、原子性、有序性

    关键字:Java内存模型(JMM)、线程安全、可见性、原子性、有序性 1.线程安全(JMM) 多线程执行某个操作的...

  • java内存结构和java内存模型

    java内存模型:与多线程JMM,就是线程可见性有关java内存结构:JVM虚拟机存储空间 class文件被类加载...

  • 第三章_Java 内存模型

    Happens - before 原则 happens-before 原则用来阐述操作之间的内存可见性,在 JMM...

  • 再学JMM Happens-Before原则

    JMM就使用happens-before的概念来阐述多线程之间的内存可见性 1. 为什么就会存在可见性问题? 相对...

  • 【再读Java并发编程】对象的共享

    并发的两层作用:1 多线程互斥访问;2 内存可见性;示例一:没有并发保护的可见性问题。 *JMM 重排序*在没有同...

网友评论

    本文标题:JMM与可见性

    本文链接:https://www.haomeiwen.com/subject/kdfhthtx.html