Java静态初始化程序是否安全?

问题

我正在使用静态代码块来初始化我所拥有的注册表中的某些控制器。因此,我的问题是,我可以保证这个静态代码块只在首次加载类时才会被调用一次吗?我知道我无法保证何时会调用此代码块,我猜它是在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) {
        // ...
    }
}

#1 热门回答(185 赞)

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

但是,如果要确保在确保仅由单个类加载器加载类之后确实执行代码。每个类加载器执行一次静态初始化。


#2 热门回答(11 赞)

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

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类


#3 热门回答(4 赞)

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

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