Потоковые безопасные синглэты в Java
В статье Википедии о Singletons упоминаются несколько потокобезопасных способов реализации структуры на Java. Для моих вопросов позвольте рассмотреть синглтоны, которые имеют длительные процедуры инициализации и получают сразу несколько потоков.
Во-первых, этот безответный метод потокобезопасен, и если да, то что он синхронизирует?
public class Singleton {
private Singleton instance;
private Singleton() {
//lots of initialization code
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
Во-вторых, почему следующий поток реализации безопасен и ленив при инициализации? Что именно происходит, если два потока входят в метод getInstance()
в то же время?
public class Singleton {
private Singleton() {
//lots of initialization code
}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
Наконец, во втором примере, если один поток получает экземпляр первым, а другой поток получает экземпляр и пытается выполнить действия над ним до того, как конструктор закончил в первом потоке? Можете ли вы попасть в небезопасное состояние?
Ответы
Ответ 1
Ответ 1: static synchronized
методы используют объект класса как блокировку - то есть в этом случае Singleton.class
.
Ответ 2: Java-язык, среди прочего:
- загружает классы при первом доступе/использовании
- гарантирует, что до того, как доступ к классу разрешен, все статические инициализаторы завершили
Эти два факта означают, что внутренний статический класс SingletonHolder
не загружается до тех пор, пока не будет вызван метод getInstance(). В этот момент и до того, как потоку, вызывающему вызов, предоставляется доступ к нему, статический экземпляр этого класса создается как часть загрузки класса.
Это означает, что у нас есть безопасная ленивая загрузка, и без необходимости синхронизации/блокировки!
Этот шаблон является шаблоном для использования в одиночных играх. Он превосходит другие шаблоны, потому что MyClass.getInstance()
является стандартным отраслевым стандартом для синглтонов - каждый, кто его использует, автоматически знает, что они имеют дело с singleton (с кодом, всегда хорошо, чтобы быть очевидным), поэтому этот шаблон имеет правильный API и правильная реализация под капотом.
btw Статья Билла Пьюга стоит прочитать для полноты при понимании одноэлементных моделей.