В последнее время я смотрю на множество сторонних библиотечных кодов и вижу этот код, который меня смущает
Итак, вот фрагмент кода из статического метода getDefault() EventBus, который возвращает статический экземпляр класса EventBus.
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
Я вижу, что код сначала проверяет, является ли экземпляр пустым, а затем в синхронизированном блоке снова делает ту же проверку еще раз. Это почему.
Что если я напишу это так?
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance = new EventBus();
}
}
return instance;
}
Что-то не так с моей версией? Что мне здесь не хватает?
1 ответ
В вашем коде два потока могут попасть внутрь оператора if одновременно, когда instance равен null. Затем один поток входит в синхронизированный блок для инициализации экземпляра, а другой блокируется. Когда первый поток выходит из синхронизированного блока, ожидающий поток входит и создает еще один объект Singleton. Обратите внимание, что когда второй поток входит в синхронизированный блок, он не проверяет, является ли экземпляр ненулевым.
Итак, мы следуем двойной проверке инициализации, она включает в себя:
- Проверьте, что переменная инициализирована (без получения блокировки). Если он инициализирован, немедленно верните его.
- Получить замок.
- Дважды проверьте, была ли переменная уже инициализирована: если другой поток сначала получил блокировку, он, возможно, уже выполнил инициализацию. Если это так, верните инициализированную переменную.
- В противном случае инициализируйте и верните переменную.