029-JVM-volatile和指令重排

194 阅读2分钟

volatile 可以防止,指令重拍,这个大多数小伙伴是知道的,但是他防止的是哪些指令的重拍呢?如果我们放任指令重拍会产生什么严重后果呢?这个未必都知道,下面就把这个问题说清楚。

1.1 源码

package com.yuhl.c2020;

/**
 * @author yuhl
 * @Date 2020/12/30 20:25
 * @Classname Chongpai
 * @Description 指令重拍
 */
public class Chongpai {
    public static void main(String[] args) {
        Object o = new Object();
    }
}

1.2 指令码

0 new #2 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init>>
7 astore_1
8 return

1.3 指令码解释

  • 从上面的解释可看到 1,2,3 这散步是一步一步来的,但是在 jvm 的底层,2,3 步可能是乱序的,也就是说可能真正的过程是 1,3,2 或者是 1,2,3
  • 如果是 1,2,3 应该不会出现什么问题,如果是 1,3,2 那么就可能会出大问题,出什么问题呢?

2.1 双重检查单例的代码实现

package com.yuhl.c2020;

/**
 * @author yuhl
 * @Date 2020/12/30 20:44
 * @Classname SinDobleCheck
 * @Description 单例模式,双重检查,特别关注volatile
 */
public class SinDobleCheck {

    private static int count = 100;
    private static volatile SinDobleCheck INSTANCE; //JIT

    private SinDobleCheck() {
    }

    public static SinDobleCheck getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (SinDobleCheck.class) {
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new SinDobleCheck();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(SinDobleCheck.getInstance().hashCode());
            }).start();
        }
    }
}

2.2 如果不用 volatile 问题在哪里

  • 通过图中 7 点可以看出不实用 volatile 会出现的问题。
  • 如果使用了 volatile 就会防止指令重拍,那么执行的顺序一定是 1,2,3,4,7,5,6 这样就是安全的。

在多线程中 volatile 尤其重要,在写代码的时候需要特别关注!

自律的艰辛总甜过懊悔的苦果!
专注于 java 后端技术及解决方案,善于总结,分享!
自律的艰辛总甜过懊悔的苦果!
专注于 java 后端技术及解决方案,善于总结,分享!
自律的艰辛总甜过懊悔的苦果!
专注于 java 后端技术及解决方案,善于总结,分享!