我们都知道下面的 DclSingleton.getInstance()是线程安全的。
public class DclSingleton {
    private static volatile DclSingleton instance;
    public static DclSingleton getInstance() {
        if (instance == null) {
            synchronized (DclSingleton .class) {
                if (instance == null) {
                    instance = new DclSingleton();
                }
            }
        }
        return instance;
    }
    // private constructor and other methods...
}
但是我们去掉 volatile 的话,其它线程就有可能获取到 partially initialized instance 。
不过我有个疑问,要证明“加了 volatile 就是线程安全的,没加就不是线程安全”的话,测试代码应该怎么写呢?
我不是问有什么更好的写法,我是问怎么证明线程安全。因为Double-Checked Locking with Singleton | Baeldung已经讲了几种替代方案了。
|  |      1edimetia3d      2022-05-09 22:09:58 +08:00 我是 C++ boy,  不太懂 java ,也不太懂 java 的内存模型,不过这里的 partial initialized 应该是指 instance 已经不是 null ,但是 DclSingleton()的构造函数还没有执行完。 思路上说,写一个特别耗时的构造函数,最后一步才更新一个 this.inited = True , 然后另外一个线程读取到 instance 不为 null,且 instance.inited = False 就行。 | 
|      2thinkershare      2022-05-09 22:13:56 +08:00 volatile 只是告诉 runtime 执行适合需要插入指令禁止乱序执行和缓存 instance 的引用地址(尽可能每次引用 instance 都需要从内存中去读取最新的值), 你要证明这个其实添麻烦的, 因为本质上是要撞运气, 至于你现在写的这个代码应该是不会存在 partially initialized instance 的, 因为 new DclSingleton()被正常构造完毕前, instance 是不会获得引用的, 除非你的 DclSingleton 是一个需要需要后序初始化的操作. | 
|      3lmshl      2022-05-09 22:19:23 +08:00 改换思路,把 static 去了,换成一个对象的属性和方法。 写 JMH 循环创建几百万次,每次都多线程访问 getInstance ,记录下对象不相等的时刻。 | 
|      4Jooooooooo      2022-05-09 22:21:02 +08:00 很大可能, intel 的芯片上因为 cpu 足够强的一致性设计, 不加 volatile 也没事. | 
|  |      5heiher      2022-05-09 22:25:11 +08:00 via Android 不加 volatile ,就可能发生 DclSingleton 对象的 filed 初始化赋值的 store 与 instance 的赋值的 store 乱序。测试逻辑就基于这个情况检测就行了,DclSingletion 增加 field 在构建函数中赋值,启动一批线程跑 getInstance 保存在局部变量,当获得的 instance 非空时,读 field 判断是否为有效赋值,最后再将 instance 复位为 null(也可专门开个复位线程来干)。这种要硬件乱序才能出问题的测试,很少次数就靠运气,但只要在弱内存序架构上多跑一会肯定会有的。 | 
|  |      6wolfie      2022-05-09 22:29:49 +08:00 循环跑呗,没轮重置 instance 为 null 。跑到重复为止。 | 
|  |      7heiher      2022-05-09 22:33:11 +08:00 via Android 我觉得你想优化掉 instance 的 volatile 减少非首次 getInstance 的开销,有个办法但要浪费点空间: ```java public class DclSingleton { private static DclSingleton instance; private static volatile DclSingleton _instance; public static DclSingleton getInstance() { if (instance == null) { synchronized (DclSingleton .class) { if (instance == null) { _instance = new DclSingleton(); instance = _instance; } } } return instance; } // private constructor and other methods... } ``` 记得也有 API 可以直接插入内存屏障,如果可以更好。 | 
|      8Suddoo      2022-05-09 23:07:59 +08:00  1 这些复杂的写法还有存在的意义吗?  Java5 之后 enum 这种特殊的 class 出现了,要实现单例,就一行代码啊,单例的本质是限制内存中 class 的个数,enum 就可以干这个事 public enum Foo { INSTANCE; } https://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java 编程本来是一件挺简单的事 | 
|  |      9heiher      2022-05-09 23:25:34 +08:00 via Android | 
|  |      10dreamlike      2022-05-09 23:46:57 +08:00 via Android  2 https://github.com/openjdk/jcstress openjdk 推荐测试用的是这个 | 
|      11Suddoo      2022-05-10 09:57:12 +08:00 via iPhone | 
|  |      12heiher      2022-05-10 11:10:58 +08:00 @Suddoo #11 针对不要机理能跑就行的,我说了语法糖甜。针对需要机理的,我也说了展开的实现相对更容易了解。我能感受到你这段话的本意,但基本道理上并没有错。 | 
|  |      14zjp      2022-05-15 19:33:06 +08:00  1 |