【问题标题】:Broken singleton without volatile example没有易失性示例的破碎单例
【发布时间】:2019-09-03 11:20:11
【问题描述】:

我知道,in theory,要实现正确的单例,除了双重检查锁定和synchronized,我们应该创建一个实例字段volatile

但在现实生活中,我无法举出一个例子,这会暴露问题。也许有一个 JVM 标志会禁用某些优化,或者允许运行时进行这种代码排列?

这是代码,据我所知,它应该不时打印到控制台,但它没有:

class Data {
    int i;

    Data() {
        i = Math.abs(new Random().nextInt()) + 1; // Just not 0
    }
}

class Keeper {
    private Data data;

    Data getData() {
        if (data == null)
            synchronized (this) {
                if (data == null)
                    data = new Data();
            }
        return data;
    }
}

@Test
void foo() throws InterruptedException {
    Keeper[] sharedInstance = new Keeper[]{new Keeper()};
    Thread t1 = new Thread(() -> {
        while (true)
            sharedInstance[0] = new Keeper();
    });
    t1.start();
    final Thread t2 = new Thread(() -> {
        while (true)
            if (sharedInstance[0].getData().i == 0)
                System.out.println("GOT IT!!"); // This actually does not happen
    });
    t2.start();
    t1.join();
}

有人可以提供一个代码,清楚地证明所描述的理论上缺乏volatile 问题吗?

【问题讨论】:

    标签: java multithreading singleton volatile


    【解决方案1】:

    关于它的非常好的文章 https://shipilev.net/blog/2014/safe-public-construction/

    你可以在最后找到例子。

    并注意

    x86 是 Total Store Order 硬件,这意味着存储对于所有处理器都以某种总顺序可见。也就是说,如果编译器实际上以相同的顺序将程序存储呈现给硬件,我们可以合理地确定实例字段的初始化存储在看到对对象本身的引用之前是可见的。即使您的硬件是完全存储排序的,您也不能确定编译器不会在允许的内存模型规范内重新排序。如果在本实验中关闭 -XX:+StressGCM -XX:+StressLCM,所有情况都会显示正确,因为编译器没有重新排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多