123

Я использую статический блок кода для инициализации некоторых контроллеров в реестре, который у меня есть. Поэтому мой вопрос: могу ли я гарантировать, что этот статический блок кода будет абсолютно вызван один раз, когда класс будет загружен первым? Я понимаю, что не могу гарантировать, когда этот кодовый блок будет вызван, я угадываю его, когда Classloader сначала загружает его. Я понимаю, что могу синхронизировать класс в статическом блоке кода, но я предполагаю, что это на самом деле то, что происходит в любом случае?Являются ли статические инициализаторы Java безопасными?

Простой пример кода;

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) { 
     // ... 
    } 
} 
+9

Мне не нравится этот дизайн, так как он не подлежит проверке. Посмотрите на Injection Dependency. – dfa

ответ

178

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

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

+2

:-) ясно и прямо в точку - приветствия! – simon622

+1

Однако класс может быть загружен несколькими загрузчиками классов, поэтому addController все равно может вызываться более одного раза (независимо от того, синхронизирован ли вы звонок) ... –

+4

Ах, тогда мы говорим, что статический блок кода фактически вызывается для каждого загрузчика классов, который загружает класс.? Хм ... Я думаю, это все равно должно быть в порядке, однако, мне интересно, как работает этот вид кода в OSGI env, с mulitple bundle classloaders .. – simon622

3

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

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

0

Да, статические инициализаторы запускаются только один раз. Read this for more information.

+1

Нет, их можно запустить более одного раза. –

+2

Нет, они могут быть запущены один раз PER CLASSLOADER. – ruurd

1

Да, вроде

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

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

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

+3

Существует очень определенный порядок, в котором они называются: по порядку в исходном коде. – mafu

+0

Кроме того, они всегда называются, независимо от того, используете ли вы их результат. Если это не было изменено в Java 6. – mafu

+7

Внутри класса инициализаторы следуют коду. Учитывая два или более классов, это не так определено, какой класс сначала инициализируется, независимо от того, инициализируется ли один класс на 100% до начала другого запуска, или как вещи чередуются. Например. если два класса имеют статичные инициализаторы, ссылающиеся друг на друга, все становится ужасно быстрым. Я думал, что есть способы, с помощью которых можно было бы ссылаться на статический окончательный int на другой класс без вызова инициализаторов, но я не буду спорить о точке так или иначе. – Matt

10

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

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

+18

Вы основываете этот ответ на том факте, что статический блок будет выполняться только один раз во всем мире - это вопрос, который был задан. –

+2

Я думаю, что это тоже небезопасно в многоклассовой среде загрузчика, что сказать.? – Ahmad

+0

@ Амад - насколько я понимаю, да. Ты прав. –

-2

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