Первый метод, который вы используете, не является потокобезопасным. Я бы посчитал это ошибкой.
Второй способ проще, поточнобезопасен, быстр и, если вы уверены, что конструктор не будет выдавать глупые исключения, исправьте.
Если вам абсолютно необходимо больше логики, вы можете использовать первый метод, убедитесь, что вы защитили его мьютексом. Что-то вроде:
public class MySingleton {
private static final Object mylock = new Object();
private static MySingleton instance;
public static MySingleton getInstance() {
synchronized(mylock) {
if (instance == null) {
instance = new MySingleton();
}
return instance;
}
}
}
Очевидно, что код более сложный, использует больше памяти, он медленнее, вы не можете объявить переменную как final ...
Оба метода будут инициализировать Singleton лениво. В Java все инициализаторы переменных и статические конструкторы задействуются загрузчиком классов при использовании класса, а не в начале кода. Если ваш путь к коду никогда не вызывает getInstance, Singleton никогда не будет инициализирован.
Лично я избегаю синглетонов, но когда я их использую, всегда с немедленным выделением в объявлении переменной.
Исправление
Я провел несколько экспериментов, и оказалось, что инициализация класса происходила параллельно с выполнением основного потока. Он не ожидал 1017, как я полагал. По крайней мере, в очень упрощенном тестовом сценарии инициализация выполняется быстро, но асинхронно.