140

我正在使用静态代码块来初始化我拥有的注册表中的一些控制器。因此,我的问题是,我可以保证这个静态代码块在类第一次加载时绝对只会被调用一次吗?我知道我无法保证何时调用此代码块,我猜是 Classloader 第一次加载它的时间。我意识到我可以在静态代码块中的类上同步,但我猜这实际上是怎么回事?

简单的代码示例是;

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

还是我应该这样做;

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}
4

6 回答 6

203

是的,Java 静态初始化器是线程安全的(使用您的第一个选项)。

但是,如果您想确保代码只执行一次,您需要确保该类仅由单个类加载器加载。每个类加载器执行一次静态初始化。

于 2009-05-18T16:43:44.947 回答
13

这是一个可以用于延迟初始化的技巧

enum Singleton {
    INSTANCE;
}

或用于 Java 5.0 之前的版本

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

由于 SingletonHolder 中的静态块将以线程安全的方式运行一次,因此您不需要任何其他锁定。只有在调用 instance() 时才会加载 SingletonHolder 类

于 2009-05-18T18:57:24.770 回答
4

在通常情况下,静态初始化程序中的所有内容都发生在使用该类的所有内容之前,因此通常不需要同步。但是,静态初始化程序调用的任何东西都可以访问该类(包括导致调用其他静态初始化程序)。

一个类可以由一个加载的类加载,但不一定要立即初始化。当然,一个类可以被多个类加载器实例加载,从而成为多个同名的类。

于 2009-05-18T16:44:44.070 回答
3

是的,有点

初始化程序只被调用一次static,因此根据该定义,它是线程安全的——您需要两次或多次调用static初始化程序才能获得线程争用。

也就是说,static初始化程序在许多其他方面令人困惑。实际上没有指定的调用顺序。如果您有两个static初始化器相互依赖的类,这会变得非常混乱。如果您使用一个类但不使用static初始化程序将设置的内容,则不能保证类加载器将调用静态初始化程序。

最后,请记住您正在同步的对象。我意识到这并不是您真正要问的,但请确保您的问题并不是真正询问您是否需要使addController()线程安全。

于 2009-05-18T16:50:59.707 回答
0

是的,静态初始化程序只运行一次。 阅读此内容以获取更多信息

于 2009-05-18T16:49:53.467 回答
-4

所以基本上,既然你想要一个单例实例,你应该或多或少地用老式的方式来做,并确保你的单例对象只初始化一次。

于 2015-05-12T22:16:41.990 回答