Являются ли Java статические инициализаторы потокобезопасными? - PullRequest
134 голосов
/ 18 мая 2009

Я использую статический блок кода для инициализации некоторых контроллеров в реестре, который у меня есть. Поэтому мой вопрос: могу ли я гарантировать, что этот статический блок кода будет вызываться только один раз при первой загрузке класса? Я понимаю, что не могу гарантировать, когда будет вызван этот блок кода, я предполагаю, что когда 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) {
        // ...
    }
}

Ответы [ 6 ]

195 голосов
/ 18 мая 2009

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

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

11 голосов
/ 18 мая 2009

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

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 будет запускаться один раз в поточно-безопасном режиме, вам не нужны никакие другие блокировки. Класс SingletonHolder будет загружен только при вызове instance ()

4 голосов
/ 18 мая 2009

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

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

3 голосов
/ 18 мая 2009

Да, вроде

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

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

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

0 голосов
/ 13 мая 2015

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

0 голосов
/ 18 мая 2009

Да, статические инициализаторы запускаются только один раз. Прочитайте это для получения дополнительной информации .

...