Являются ли статические инициализаторы 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

Да, статические инициализаторы Java являются потокобезопасными (используйте свой первый вариант).

Однако, если вы хотите убедиться, что код выполняется точно, как только вам нужно убедиться, что класс загружен только одним загрузчиком классов. Статическая инициализация выполняется один раз для класса-загрузчика.

Ответ 2

Это трюк, который вы можете использовать для ленивой инициализации

enum Singleton {
    INSTANCE;
}

или для предварительной версии 5.0 5.0

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

Поскольку статический блок в SingletonHolder будет запускаться один раз в потоковом безопасном режиме, вам не нужна никакая другая блокировка. Класс SingletonHolder будет загружаться только при вызове instance()

Ответ 3

В обычных обстоятельствах все в статическом инициализаторе происходит - перед тем, что использует этот класс, поэтому синхронизация обычно не требуется. Тем не менее, класс доступен для всего, что вызвал статический intailiser (включая вызовы для запуска других статических инициализаторов).

Класс может быть загружен классом, загруженным, но не обязательно инициализированным сразу. Конечно, класс может быть загружен экземплярами мультипликаторов классов и, таким образом, становится несколькими классами с тем же именем.

Ответ 4

Да, вроде

static инициализатор получает только один раз, поэтому по этому определению он потокобезопасен - вам понадобится два или более вызовов static инициализатора, чтобы даже получить конфликт потоков.

Тем не менее, static инициализаторы сбивают с толку многими другими способами. Там действительно нет определенного порядка, в котором они вызваны. Это становится очень запутанным, если у вас есть два класса, static инициализаторы которых зависят друг от друга. И если вы используете класс, но не используете то, что установит static инициализатор, вам не гарантируется, что загрузчик классов вызовет статический инициализатор.

Наконец, имейте в виду объекты, на которые вы синхронизируете. Я понимаю, что на самом деле это не то, о чем вы просите, но убедитесь, что ваш вопрос не спрашивает, нужно ли делать addController() поточно-безопасным.

Ответ 6

Итак, в основном, поскольку вам нужен экземпляр singleton, вы должны сделать это более или менее старомодным способом и убедиться, что ваш одноэлементный объект инициализирован один раз и только один раз.