【问题标题】:How to implements DCL without the key work volatitle?如何在没有关键工作volatitle的情况下实现DCL?
【发布时间】:2021-05-05 12:13:04
【问题描述】:
public  class DCL {
    private static DCL staticDcl;

    private final DCL finalDcl;

    private DCL(){
        //other init operation
        
        
        //the action must be last.
        finalDcl = this;
    }

    public static DCL getDCL() {

        if (staticDcl == null) {
            synchronized(DCL.class) {
                if (staticDcl == null) {
                    staticDcl = new DCL();
                }
            }
        }
        return staticDcl.finalDcl;
    }
}

上面的代码在多线程环境下能否正常运行??

我想用关键字final来实现dcl而不是volatitle。

【问题讨论】:

  • 您犯了一个常见错误,即未将读取的值存储到局部变量中。一个线程可能会将staticDcl == null 评估为false,因为另一个线程将非null 值写入staticDcl,因此,跳过synchronized 块并从@ 中的staticDcl 读取null 987654329@语句,由于没有同步,两次读操作没有先后顺序。

标签: java multithreading concurrency jvm


【解决方案1】:

是的,没有volatileit is potentially broken,你可以阅读我的其他答案以获得解释。

你有staticDclfinalDcl 是相当奇怪的。如果您想通过 DCL 提供单例,则不需要两者。您可以阅读this 问题的开头,了解如何实现这一点。它还讨论了如何在没有volatile 的情况下实现双重检查锁定安全,但仍然使用一些特殊的语义,例如release/acquire

【讨论】:

  • 是的。你说的对。但我只想知道我们能否用 final 实现 dcl 单例。
  • 我会用staticDcl提供全局引用,避免这个点逃逸。
  • @xanxus 你知道你可以做到private static final DCL staticDcl = new DCL(); 并拥有public static DCL getDCL() { return staticDcl; } 并完成它,对吧?你确实不需要需要其他任何东西。
  • 是的,我知道。这是个好主意。我在工作中经常使用这种方法。
  • 我的目的不是找到实现 dcl singleton 的好方法。只是想知道我们可以用 final 实现它吗?为什么或如何实现?
【解决方案2】:

可能遇到问题,请参阅Why is volatile used in double checked locking?

我建议不要使用这种模式,而是使用static holder pattern (SHP),它可以保证您尝试重现的所有机制。使用 SHP,您甚至不必考虑 volatile,因为 JVM 会直接为您处理。

public class DCL {
    private DCL(){
        //other init operation
    }

    public static DCL getDCL() {
        return Holder.INSTANCE;
    }

    private static final class Holder {
        private static final DCL INSTANCE = new DCL();
    }
}

还要注意private final DCL finalDcl; 是多余的,因为您可以直接返回实例,无需创建等于this 的新字段。

这种模式的好处是 INSTANCE 仅在它第一次被引用时被实例化,即当 getDCL() 被调用时。这是由于lazy class loading 的性质造成的。

【讨论】:

  • 不要混淆加载、链接和初始化。 DCL 实例将被创建 when the class Holder is initialized。由于这个惰性初始化对所有类都是一样的,所以你不需要Holder类,但是可以将static final字段直接放到DCL类中,当第一次调用getDCL()时就会进行初始化制作。只有当该类有其他非私有静态成员可能比预期更早触发初始化时,您才需要一个持有者类。
  • 对不起,我不同意“你可以让线程初始化 staticDcl 字段,而另一个线程也这样做,因为他们不知道 staticDcl 已更新。”因为同步可以确保避免它.
  • @Lino 抱歉,我不能同意“您可以让线程初始化 staticDcl 字段,而另一个线程也这样做,因为他们不知道 staticDcl 已更新。”因为同步可以确保避免它。
  • @Holger 可以给你看代码吗?我认为他的演示是正确的!因为 Holder 是静态内部类。所以Holder类在DCL类初始化时不会初始化,它发生在Holder.INSTANCE时。
  • @xanxus 这个答案的代码是正确的。但是关于“延迟加载”的解释并非如此,因为该字段是在指定的类初始化时间初始化的(按照我之前评论的链接)。您可以在具有急切类加载的 JVM 上运行此代码,它仍然可以按预期工作。一旦你按照我的链接阅读了规范,你就会明白你不需要内部类,因为延迟初始化也适用于 DCL 类。该示例仍然是正确的,但可以简化。
猜你喜欢
  • 2018-09-25
  • 1970-01-01
  • 2021-01-20
  • 2010-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多