Вы связались с ИТ-отделом своего университета? У моей жены была аналогичная проблема, и мы предположили, что это проблема Ubuntu. Однако я думаю, что сеть активно блокировала ее связь. В то время мы этого не знали, но конкретная сеть Wi-Fi, доступная моей жене, доступна только компьютерам университета или компьютерам, на которых установлено «безопасное» изображение для Windows или Mac. Если это общая сеть wi-fi, доступная студентам и т. Д., Пожалуйста, не обращайте внимания. Я думал, что я опубликую это, хотя, если вы являетесь сотрудником, пытающимся получить доступ к сети Wi-Fi сотрудника.
Если у вас его нет, второй поток может попасть в синхронизированный блок после первого установленного значения null, а ваш локальный кеш все равно будет считать его нулевым.
Первый не для правильности (если бы вы были правы, что это было бы самовыражением), а скорее для оптимизации.
Объявление переменной как volatile гарантирует, что все обращения к ней фактически будут считывать текущее значение из памяти.
Без volatile компилятор может оптимизировать доступ к памяти и сохранить его значение в регистр, поэтому только первое использование переменной считывает фактическую ячейку памяти, содержащую переменную. Это проблема, если переменная изменена другим потоком между первым и вторым доступом; первый поток имеет только копию первого (предварительно измененного) значения, поэтому второй оператор if проверяет устаревшую копию значения переменной.
Неустойчивое чтение не очень дорого само по себе.
Вы можете создать тест для вызова getInstance() в узком цикле, чтобы наблюдать влияние изменчивого чтения; однако этот тест нереалистичен; в такой ситуации программист обычно вызывал getInstance() один раз и кэшировал экземпляр для продолжительности использования.
Другой impl с помощью поля final (см. wikipedia). Для этого требуется дополнительное чтение, которое может стать дороже, чем версия volatile. Версия final может быть быстрее в узком цикле, однако этот тест является спорным, как ранее утверждалось.
Как цитируется @irreputable, volatile не дорогая. Даже если это дорого, согласованность должна быть отдана приоритету над производительностью.
Существует еще один чистый элегантный способ для Lazy Singletons.
public final class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
Исходная статья: Инициализация-по-запросу_holder_idiom из wikipedia
В разработке программного обеспечения Инициализация по требованию (дизайн шаблона) идиома - это ленивый загруженный синглтон. Во всех версиях Java идиома позволяет безопасную, очень параллельную ленивую инициализацию с хорошей производительностью. Поскольку для инициализации у класса нет никаких static переменных, инициализация завершается тривиально.
Определение статического класса LazyHolder внутри него не инициализируется до тех пор, пока JVM не определит, что LazyHolder должен быть выполнен.
Статический класс LazyHolder выполняется только тогда, когда статический метод getInstance вызывается в классе Singleton, и в первый раз это произойдет, JVM будет загружать и инициализировать класс LazyHolder.
Это решение является потокобезопасным, не требуя специальных языковых конструкций (т. е. volatile или synchronized).