代码之家  ›  专栏  ›  技术社区  ›  Ludvig W

Java枚举垃圾收集和枚举变量差异

  •  1
  • Ludvig W  · 技术社区  · 6 年前

    我有一个关于java垃圾收集和枚举类型的问题。 假设我有一个这样的枚举:

    enum ConnectionHelper {
    INSTANCE;
    
    private boolean initialized = false;
    private static int someVar;
    
    
    ConnectionHelper initialize() {
        if (!initialized) {
            // Do some initialisation...
            someVar = 10;
    
            // NOTE: 1
            initialized = true;
        }
        return this;
    }
    void start() {
        // NOTE: 2
        if (!initialized) {
            throw new IllegalStateException("ConnectionHelper has to be initialized.");
        }
    
        // do some work...
    }
    
    1. 现在有没有一个场景,由于垃圾收集器的原因,initialized可能会返回到false?我问的原因是,如果是这样的话,我需要对这种情况采取额外的预防措施。

    2. 而且,如果我用一个枚举来表示en singleton,对于state使用静态变量还是非静态变量有关系吗?例如,在这个例子中,有两个变量:someVar和initialized,如果initialized也是静态的,那么问题1会有什么不同吗或者两者都是非静态的?

      谢谢!

    2 回复  |  直到 6 年前
        1
  •  3
  •   Holger    6 年前

    我们可以回答各种各样的问题:垃圾收集器是否会使这个程序的行为异常,一般来说没有。垃圾收集器的目的就是清除 未使用 对象是透明的,甚至在程序没有注意到的情况下产生一种可观察的行为,就像变量从 true false 绝对超出了垃圾收集器允许的操作范围。

    也就是说,你的程序不是线程安全的。如果多个线程访问 ConnectionHelper 如果没有额外的同步,他们可能会看到不一致的结果,包括 对于 initialized 变量,而另一个线程已经看到 真的 从外部时钟的角度来看,或者 真的 已初始化 但仍然没有看到 10 写给 someVar 是的。

    解决办法很简单不要实现多余的惰性初始化这个 enum 常量在类初始化期间初始化,这已经是惰性的(如 JLS §12.4.1 )使线程安全(如 JLS §12.4.2 )通过jvm。

    enum ConnectionHelper {
        INSTANCE;
        private static final int someVar = 10;
        void start() {
        // do some work, use the already initialized someVar...
        }
    }
    

    enum ConnectionHelper {
        INSTANCE;
        private final int someVar = 10;
        void start() {
        // do some work, use the already initialized someVar...
        }
    }
    

    enum ConnectionHelper {
        INSTANCE;
        private final int someVar;
        ConnectionHelper() {
            someVar = 10; // as place-holder for more complex initialization
        }
        void start() {
        // do some work, use the already initialized someVar...
        }
    }
    

    不管您是否声明变量 static 或者不。

    第一个线程调用 start() 将执行类初始化,包括 萨默瓦 是的。如果其他线程在初始化仍在进行时调用该方法,则它们将等待该方法完成。初始化完成后,所有线程都可以执行 START() 方法使用初始化的值而不减慢速度。

        2
  •  1
  •   Boris Schegolev    6 年前

    枚举类型不需要垃圾收集。你的 ConnectionHelper.INSTANCE 无法取消引用,因此对象在实例化后将始终保留在内存中。

    所以,对于你的问题:

    广告1:不,不能还原。唯一能让它回到 false 是手动设置的。

    广告2:一个单身汉没有区别。如果你有更多的实例,他们会分享 static 变量,而不是通常的变量。