指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。编译器、处理器也遵循这样一个目标。注意是单线程。多线程的情况下指令重排序就会给程序员带来问题。

是为了保证单线程的运行效率,可以进行指令优化,不影响单线程的执行效果

但是指令重排在多线程之中会出现问题

JVM指令重排导致Singleton双重锁定出错

public class Singleton {
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton(); //非原子操作
}
}
}
return instance;
}
}

看似简单的一段赋值语句:instance = new Singleton();,其实JVM内部已经转换为多条指令:

memory = allocate(); //1:分配对象的内存空间

ctorInstance(memory); //2:初始化对象

instance = memory; //3:设置instance指向刚分配的内存地址

但是经过重排序后如下:

memory = allocate(); //1:分配对象的内存空间

instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化

ctorInstance(memory); //2:初始化对象

可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。

修改为如下的形式:

public class Singleton {

    private static  volatile Singleton instance = null;

    private Singleton() { }

    public static Singleton getInstance() {

    if(instance == null) {

    synchronized (Singleton.class) {
if(instance == null) { instance = new Singleton(); } } } return instance; } }

除了前面内存可见性中讲到的volatile关键字可以保证变量修改的可见性之外,还有另一个重要的作用:在JDK1.5之后,可以使用volatile变量禁止指令重排序。但是上面代码在jdk 1.5之前还是有问题的

注意,前面反复提到“从语义上讲是没有问题的”,但是很不幸,禁止指令重排优化这条语义直到jdk1.5以后才能正确工作。此前的JDK中即使将变量声明为volatile也无法完全避免重排序所导致的问题。所以,在jdk1.5版本前,双重检查锁形式的单例模式是无法保证线程安全的。

单例模式最还采用静态内部类的方式来实现,是最经典的也是保证线程安全的

package com.qqyumidi;

public class SingleTon {

    // 利用静态内部类特性实现外部类的单例
private static class SingleTonBuilder {
private static SingleTon singleTon = new SingleTon();
} // 私有化构造函数
private SingleTon() { } public static SingleTon getInstance() {
return SingleTonBuilder.singleTon;
} public static void main(String[] args) {
SingleTon instance = getInstance();
}
}

java 虚拟机指令重新排序的更多相关文章

  1. java虚拟机指令dup的理解

    举个例子: public class ExceptionTest{ void cantBeZero(int i) throws Exception{ throw new Exception(); } ...

  2. [三] java虚拟机 JVM字节码 指令集 bytecode 操作码 指令分类用法 助记符

    说明,本文的目的在于从宏观逻辑上介绍清楚绝大多数的字节码指令的含义以及分类 只要认真阅读本文必然能够对字节码指令集有所了解 如果需要了解清楚每一个指令的具体详尽用法,请参阅虚拟机规范 指令简介 计算机 ...

  3. 深入理解java虚拟机(六)字节码指令简介

    Java虚拟机指令是由(占用一个字节长度.代表某种特定操作含义的数字)操作码Opcode,以及跟随在其后的零至多个代表此操作所需参数的称为操作数 Operands 构成的.由于Java虚拟机是面向操作 ...

  4. Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap

    Atitit.java 虚拟机的构成 与指令分类 与 指令集合 以及字节码查看工具javjap 1.1. 虚拟机的构成 java虚拟机--处理器.堆栈.寄存器.指令系统. 1 1.2. 虚拟机执行过程 ...

  5. 翻译Java虚拟机的结构

    英文原版:  https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 直接谷歌翻译: Java SE规范 > Java虚拟机 ...

  6. Java虚拟机--虚拟机编译器

    void sspin() { short i; for (i = 0; i < 100; i++) { ; // Loop body is empty }} Method void sspin( ...

  7. Java虚拟机--字节码指令集

    1. 字节码指令集简介: Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的操作码(Opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(Operands)所构成.虚拟机中许多指 ...

  8. 深入Java虚拟机——类型装载、连接(转)

    来自http://hi.baidu.com/holder/item/c38abf02de14c7d31ff046e0 Java虚拟机通过装载.连接和初始化一个Java类型,使该类型可以被正在运行的Ja ...

  9. 深入Java虚拟机(3)——安全

    因为网络允许多台计算机共享数据和分布式处理,所以它提供了一条入侵计算机系统的潜在途径,使得其他人可以窃取信息,改变或破坏信息,盗取计算机资源等等.为了解决由网络引起的安全问题,Java体系结构采用了一 ...

随机推荐

  1. STM32串口DMA接收数据错位——暴力解决方法

    背景:两片STM32通过串口通信,为了减小CPU负担,采用DMA进行通信,发送端为STM32F103C8T6,接收端为STM32F407VET6.在调试的过程中发现,一直出现数据错位的问题,接收端尝试 ...

  2. 【HBase】表的version

    建表.添加数据 Examples: hbase> create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40'] hbase> c ...

  3. JavaSE(三) 变量与运算符

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 2 变量的使用 2.1按数据类型分类 ​ 整型 : byte(1字节 = 8bit) short(2字节 ...

  4. Java实现 LeetCode 672 灯泡开关 Ⅱ(数学思路问题)

    672. 灯泡开关 Ⅱ 现有一个房间,墙上挂有 n 只已经打开的灯泡和 4 个按钮.在进行了 m 次未知操作后,你需要返回这 n 只灯泡可能有多少种不同的状态. 假设这 n 只灯泡被编号为 [1, 2 ...

  5. Java实现 蓝桥杯 算法训练 Torry的困惑(基本型)

    算法训练 Torry的困惑(基本型) 时间限制:1.0s 内存限制:512.0MB 问题描述 Torry从小喜爱数学.一天,老师告诉他,像2.3.5.7--这样的数叫做质数.Torry突然想到一个问题 ...

  6. Java实现 LeetCode 392 判断子序列

    392. 判断子序列 给定字符串 s 和 t ,判断 s 是否为 t 的子序列. 你可以认为 s 和 t 中仅包含英文小写字母.字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符 ...

  7. Java实现蓝桥杯模拟正整数序列的数量

    问题描述 小明想知道,满足以下条件的正整数序列的数量: 1. 第一项为 n: 2. 第二项不超过 n: 3. 从第三项开始,每一项小于前两项的差的绝对值. 请计算,对于给定的 n,有多少种满足条件的序 ...

  8. Java中StringBuffer类的常用方法

    StringBuffer:StringBuffer类型 描述:在实际应用中,经常回遇到对字符串进行动态修改.这时候,String类的功能受到限制,而StringBuffer类可以完成字符串的动态添加. ...

  9. Java实现 洛谷 P1008 三连击

    public class Main { public static void main(String[] args){ for(int i = 123; i <= 329; i++){ int[ ...

  10. java实现排座位

    ** 排座位** 要安排:3个A国人,3个B国人,3个C国人坐成一排. 要求不能使连续的3个人是同一个国籍. 求所有不同方案的总数? 参考答案: 283824 public class Main1 { ...