2015-01-13 3 views
2

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

Методы этого класса не вызываются довольно долгое время, и они всегда вызываются из класса, который автоматически загружается при запуске. Моя настройка такова:

class SlowToStartUp { 
    public static void init() { 
    // do nothing 
    } 

    static { 
    initiateConnectionToServer() 
    } 

    public static V invokeServer() { 
    waitForServerToConnect(); 
    return valueFromServer(); 
    } 
} 


class AlwaysLoaded { 
    static { 
    SlowToStartUp.init(); 
    } 

    public void someMethod() { 
    V v = SlowToStartUp.invokeServer(); 
    } 

Это выглядит как конструктивно правильно. Если бы не было функции init() вообще, initiateConnectionToServer() не вызывается до тех пор, пока someMethod() не понадобится класс в первый раз, а затем будет ненужная (и в моей системе, неприемлемая) задержка.

Если я положил звонок initiateConnectionToServer() в init(), интерфейс был бы более хрупким (поскольку звонок может быть забыт).

Но теперь мне интересно, перехитрил ли я себя. Компилятор может видеть, что init() пуст. Могло ли это не просто оптимизировать вызов? Теперь это не так, но это гарантировано?

Я пробовал маркировку init() как volatile, но это недопустимо.

Я рассматриваю возможность ввода фактической инициализации в init(), убедившись, что он идемпотент, и вызывая его из статического блока, чтобы быть в безопасности, но я подумал, что сначала попрошу совета.

+0

Не могли бы вы инициализировать конструктор класса, а затем просто задержать создание экземпляра своего класса, пока он не понадобится? Это позволит избежать необходимости вызывать статический класс init(), но будет проблематичным, если вам потребуется быстрый ответ от вашего класса. – Brian

+0

Это точно. Программа запускается в полночь в пятницу. Человек использует его в понедельник утром. Я не хочу, чтобы ей приходилось ждать две минуты, пока происходит соединение. – Malvolio

+2

Можно ли создать экземпляр объекта при первом запуске программы? Если вы можете гарантировать, что никто не понадобится в течение некоторого времени, вы всегда можете дождаться завершения загрузки основного класса до вызова конструктора. – Brian

ответ

0

У меня есть тонкий ответ на мой собственный вопрос. События, инициирующие инициализацию классов, указаны в JLS 12.4.1.

Класс или тип интерфейса Т будет инициализирован непосредственно перед первым возникновения какой-либо одной из следующих причин:

  • Т представляет собой класс и экземпляр Т создается.
  • T - класс и статический метод, объявленный T, вызывается.
  • Статическое поле, объявленное T, назначается.
  • Статическое поле, объявленное T, используется, и поле не является постоянной переменной (§4.12.4).
  • T - класс верхнего уровня, и выполняется инструкция assert (§14.10), лексически вложенная в T.

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

Комментарии приветствуются.

+0

Статический инициализатор не совпадает с статическим методом. Статические инициализаторы запускаются при тех же условиях, что и сам класс будет инициализирован, но если в нем нет ничего, то нет причин его удерживать. Аналогично, если ваш статический метод init() пуст и никогда не вызывается, нет причин его сохранять, но уверены ли вы, что компилятор его удалит? – Brian

+0

Статическая функция I имела в виду 'init()'. Я бы подумал, что его оптимизация нарушит явное правило. Я уверен, что компилятор, который я использую прямо сейчас, не уничтожает его, но я не хочу зависеть от (потенциально) переходного поведения. – Malvolio

+0

Я бы не думал, что какой-либо компилятор оптимизирует публичный статический метод, даже если он пуст, но я не могу сказать вам со 100% уверенностью. Однако для вашего конкретного случая я бы сказал, что точка спорна: инициализируйте свой класс в конструкторе и создайте новый экземпляр при запуске программы, как обычно, так и в виде одноэлементного. У вас есть причины сделать иначе, или вас больше интересует спецификация? – Brian

3

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

public class SlowPokeSingleton { 
    private SlowPokeSingleton() { /* execute init code */ } 

    // created at startup 
    private final static SlowPokeSingleton instance = new SlowPokeSingleton(); 

    public static SlowPokeSingleton instance() { return instance; } 
} 

Вам нужно будет позвонить instance(), чтобы убедиться, что экземпляр фактически создается. Вы можете добавить это к старту вашего сервера, чтобы быть в безопасности.

+1

Я _am_ 100% уверен, что мне нужно вызвать instance(), чтобы убедиться, что экземпляр создан. Я обманывал это с помощью _days_. – Malvolio

0

Я согласен с Джованни Ботт:

Вы можете использовать одноплодную шаблон вместо статических методов и получать одноплодного экземпляр, как только ваш запуск приложения. Это будет заставить JVM создать экземпляр и таким образом запустить вашу инициализацию . - Джованни Ботта

В частности:

1) Поместите "трудоемкой" часть вашей инициализации в частный, статический "INIT) (" метод.

2) Сделайте свой конструктор класса «частным».

3) Вместо конструктора предоставить публичный статический метод getInstance() для получения ссылки на ваш (уже инициализированный) экземпляр singleton.

4) Ваши другие методы могут быть нестационарными, если хотите.

Смежные вопросы