高性能编程-21JVM内存模型详解

内存模型又来:

我们先从一个代码场景说起:


// 1、 jre/bin/server  放置hsdis动态链接库
//  测试代码 将运行模式设置为-server, 变成死循环   。 没加默认就是client模式,就是正常(可见性问题)
// 2、 通过设置JVM的参数,打印出jit编译的内容 (这里说的编译非class文件),通过可视化工具jitwatch进行查看
// -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=jit.log
//  关闭jit优化-Djava.compiler=NONE
public class VisibilityDemo {
    // 运行标志
    public boolean flag = true;

    // JIT just in time ( --  --)
    public static void main(String[] args) throws InterruptedException {
        VisibilityDemo demo1 = new VisibilityDemo();
        System.out.println("代码开始了");
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                int i = 0;
//                boolean f = demo1.flag;
//                if(f) {
//                    while(true){
//                        i++;
//                    }
//                }
                while (demo1.flag) {
                    synchronized (this) {
                        i++;
                    }
                }
                System.out.println(i);
            }
        });
        thread1.start();

        TimeUnit.SECONDS.sleep(2);
        // 设置is为false,使上面的线程结束while循环
        demo1.flag = false;
        System.out.println("被置为false了.");
    }
}

复制代码

引发的问题1:为什么这个代码不停止

  • reason1:CPU缓存造成的可能原因

引发的问题2:如何让他正确的停止

  • volatile:1.根据内存模型的规定,保持可见性。ACC_VOLATILE访问控制,可以保证没有缓存立马可见。
  • 启动参数 -client -Server重大区别在于jit优化

可见性问题

1.CPU等其他缓存,导致可见性(短时间内)

2.重排序可能导致可见性

3.三种运行模式:编译、解释、混合

  • 编译:字节码---jit编译---汇编
  • 解释: 字节码---一段段编译---汇编
  • 混合:运行的过程中,JIT编译器生效,针对热点代码进行优化
while(demo1.flag){
    i++;
}
//优化后的伪代码 --1旁敲侧击。2查看优化后的汇编代码jitwatch
boolean f = demo1.flag
if(f){
    while(true){
        i++;
    }
}
复制代码

多线程中的问题:

  • 所见非所得
  • 无法肉眼去检测程序的准确性
  • 不同的运行平台有不同的表现性
  • 错误很难重现

从内存结构到内存模型

指令重排序

Java编程语言的语义允许编译器和处理器执行优化,这些优化可以与不正确的同步代码交互,从而产生看似矛盾的行为。

内存模型的含义

内存模型描述程序的可能行为

Java编程语言内存模型通过检查执行跟踪中的每个读操作,并根据某些规则检查该操作观察到的写操作是否有效来工作。

只要程序的所有执行产生的结果都可以由内存模型预测,具体的实现者任意实现,包括操作的重新排序和删除不必要的同步。

内存模型决定了在程序的每个点上可以读取什么值

内存模型的由来

JVM运行时数据区设计,多个内存区间进行交互势必会有问题,Java语言推出了内存模型的相关规范。

shared variables 共享变量描述

可以在线程之间共享的内存称之为共享内存或堆内存 所有实例字段、静态字段和数组元素都存储在堆内存中。 如果至少有一个访问是写的,那么对同一个变量的两次访问是冲突的。

线程操作的定义

操作定义:
write 要写的变量以及要写的值。
read 要读的变量以及可见写入值(由此,我们可以确定可见的值)
lock要锁定的管程(监视器monitor)
unlock 要解锁的管程
外部操作(socket等等)
启动和终止
复制代码

程序顺序:如果一个程序没有数据竞争,那么程序的所有执行看起来都是顺序一致的 本规范只涉及线程间的操作。

对于同步的规定

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

happens-before先行发生原则

happens-before 关系主要用于强调两个有冲突的动作之间的顺序,以及定义数据争用的发生时机

具体的虚拟机实现,有必要确保以为原则的成立

  • 某个线程中的每个动作都happens-before该线程中该动作后面的动作。
  • 某个管程上的unlock动作happens-before同一个管程上后续的lock操作。
  • 对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作
  • 在某个线程对象上调用start()方法happens-before该启动了的线程中的任意动作。
  • 某个线程中的所有动作happens-before任意其他线程成功从该线程对象上的join()返回。
  • 如果某个动作a happens-before动作b ,且b happens-before动作c,则有a happens-before c

volatile关键字

可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到

根据JMM中规定的happen before和同步原则
对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作
对volatile变量v的写入,与所有其他线程后续对v的读同步
复制代码
  • 要满足这些条件,所以volatile关键字就有这些功能:
  • 1、禁止缓存:(volatile变量的访问控制符会加个ACC_VOLATILE
  • 2、对volatile变量相关的指令不做重排序。

final 在jmm中的处理

final在该对象的构造函数中设置对象的字段,当线程看到该对象,将始终看到该对象的final字段的正确构造版本。
伪代码示例:f=new finalDemo(); 读取到f.x一定最新,x为final字段
复制代码
如果在构造函数中设置字段后发生读取,则会看到该final字段分配的值,否则他将看到默认值,
伪代码示例:public finalDemo(){x=1;y=x;} y会等于1
复制代码
读取该工序对象的final成员变量之前,先要读取共享对象
伪代码示例:r = new ReferenceObj();k = r.f; 这两个操作不能重排序。
复制代码
通常static final是不可以修改的字段,然而System.in,System.out和System.err是static final字段,遗留原因,必须允许通过set方法改变,我们将这些字段称为写保护,以区别于普通final字段。
复制代码

Word Tearing字节处理

一个字段或元素的更新不得与任何其他字段或者元素的读取或者更新交互。特别是,分别更新字节数组的相邻元素的两个线程不得干涉或交互,也不需要同步以确保顺序一致性。

有些处理器(尤其是早起的alphas处理器)没有提供单个字段的功能。

在这样的处理器上更新byte数组,若只是简单的读取这个内容,更新对应的字节,然后将整个内容再写回内存,将是不合法的。

这个问题有时候被称为:"字分裂(word tearing)"在单独更新单个字节有难度的处理器上,就需要需求其他方式了。

double和long的特殊处理

猜你喜欢

转载自juejin.im/post/5e15e5f4f265da5d1a447568