Этот конкретный пример?
С резьбой все в порядке. С точки зрения стиля это плачевно. НЕ пишите такие блоки catch. Это также означает, что если возникнет исключение (здесь этого не может быть — ваш конструктор пуст), ваш код сбросит половину информации в системную ошибку, а затем продолжит работу с экземпляром нулевой ссылки экземпляра Singleton, вызывая другие код для выплевывания исключений NullPointerException (потому что код просто продолжает работать, так как вы поймали исключение, а не позволили ему произойти). Если вы обработаете все исключения таким образом, одна ошибка вызовет сотни ошибок в ваших журналах, и все они не будут иметь значения, кроме первой.
Как только вы позаботитесь об этой проблеме обработки исключений, вы можете сделать переменную final
и больше не присваивать ей значение null. Пока вы это делаете, сделайте весь класс final
. Фактически это уже (поскольку у вас есть только частный конструктор):
public final class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Single() {}
public static Singleton getInstance() {
return INSTANCE;
}
Причина, по которой это срабатывает, когда 2 потока одновременно вызывают getInstance, заключается в самом механизме загрузчика классов: загрузчик классов гарантирует, что любой данный класс никогда не загружается более одного раза одним и тем же загрузчиком, даже если 2 потока одновременно требуют this (загрузчик классов будет синхронизировать/блокировать, чтобы избежать этой ситуации), и процесс инициализации (статический блок, который был излишне запутан, как показано в приведенном выше примере) аналогичным образом защищен и не может повторяться дважды.
Это единственная халява, которую вы получаете: для статических методов, как правило, все потоки могут просто запускать один и тот же метод одновременно, если они этого хотят. И здесь они делают - просто инициализация (которая включает в себя часть ... = new Singleton();
) выполняется только один раз.
NB: Если вам нужно делать более сложные вещи, создайте вспомогательные методы:
public final class Singleton {
private static Singleton INSTANCE = create();
private Singleton(Data d) {
// do stuff with 'd'
}
private static Singleton create() {
Data d;
try {
d = readStuffFromDataIncludedInMyJar();
} catch (IOException e) {
throw new Error("states.txt is corrupted", e);
}
return new Singleton(d);
}
}
Этот:
- Сохраняет код простым - статические инициализаторы - это вещь, но довольно экзотический java.
- Облегчает тестирование вашего кода.
- Это внутренний файл; если он отсутствует/сломан, это так же вероятно/так же проблематично, как один из файлов вашего класса, который ушел на прогулку. Ошибка гарантирована здесь. Это не может произойти, если вы не написали ошибку или не испортили сборку, и жесткий сбой с явным исключением, сообщающим вам, что именно не так, - это именно то, что вы хотите, чтобы произошло в этом случае, а не для слепого продолжения кода в состоянии, когда половина ваше приложение перезаписано абракадаброй из-за сбоя диска или чего-то еще. Лучше просто заключить, что все пошло наперекосяк, так и сказать, и перестать бежать.
person
rzwitserloot
schedule
19.02.2021