Да, это необходимо. Есть несколько методов, которые можно использовать для обеспечения безопасности потоков с отложенной инициализацией:
Драконовская синхронизация:
private static YourObject instance;
public static synchronized YourObject getInstance() {
if (instance == null) {
instance = new YourObject();
}
return instance;
}
Это решение требует, чтобы каждый поток был синхронизирован, в то время как на самом деле требуется только несколько первых.
Дважды проверьте синхронизацию :
private static final Object lock = new Object();
private static volatile YourObject instance;
public static YourObject getInstance() {
YourObject r = instance;
if (r == null) {
synchronized (lock) { // While we were waiting for the lock, another
r = instance; // thread may have instantiated the object.
if (r == null) {
r = new YourObject();
instance = r;
}
}
}
return r;
}
Это решение гарантирует, что только первые несколько потоков, которые пытаются получить ваш синглтон, должны пройти через процесс получения блокировки.
Инициализация по запросу :
private static class InstanceHolder {
private static final YourObject instance = new YourObject();
}
public static YourObject getInstance() {
return InstanceHolder.instance;
}
Это решение использует гарантии модели памяти Java при инициализации класса для обеспечения безопасности потоков. Каждый класс можно загрузить только один раз, и он будет загружен только тогда, когда это необходимо. Это означает, что первый раз getInstance
вызывается, InstanceHolder
будет загружен и instance
будет создан, и, поскольку это контролируется ClassLoader
s, никакой дополнительной синхронизации не требуется.
Draconian synchronization
иDouble check synchronization
getInstance () - метод должен быть статическим!static
, но это могло бы иметь больше смысла, если бы они были. Внесены по запросу.r
не требуется для правильности. Это просто оптимизация, позволяющая избежать доступа к изменчивому полю, поскольку это намного дороже, чем доступ к локальной переменной.Этот шаблон выполняет поточно-безопасную отложенную инициализацию экземпляра без явной синхронизации!
Это работает, потому что он использует загрузчик классов, чтобы выполнить всю синхронизацию бесплатно: к классу
MySingleton.Loader
сначала обращаются внутриgetInstance()
метода, поэтомуLoader
класс загружается, когдаgetInstance()
вызывается в первый раз. Кроме того, загрузчик класса гарантирует, что вся статическая инициализация завершена до того, как вы получите доступ к классу - это то, что обеспечивает безопасность потоков.Это похоже на волшебство.
На самом деле он очень похож на шаблон enum Jhurtado, но я считаю, что шаблон enum является злоупотреблением концепцией enum (хотя он действительно работает)
источник
final
следует добавить. Готово.Если вы работаете в многопоточной среде на Java и вам нужно гарантировать, что все эти потоки обращаются к одному экземпляру класса, вы можете использовать Enum. Это будет иметь дополнительное преимущество, помогая вам справиться с сериализацией.
а затем просто пусть ваши потоки используют ваш экземпляр, например:
источник
Да, нужно
getInstance()
синхронизировать. В противном случае может возникнуть ситуация, когда можно создать несколько экземпляров класса.Рассмотрим случай, когда у вас есть два потока, которые вызывают
getInstance()
одновременно. Теперь представьте, что T1 выполняется сразу послеinstance == null
проверки, а затем запускается T2. На данный момент экземпляр не создан или не установлен, поэтому T2 пройдет проверку и создаст экземпляр. Теперь представьте, что выполнение снова переключается на T1. Теперь синглтон создан, но T1 уже проверил! Он продолжит создание объекта снова! СозданиеgetInstance()
синхронизированных предотвращает эту проблему.Есть несколько способов сделать синглтоны потокобезопасными, но сделать
getInstance()
синхронизацию, вероятно, проще всего.источник
Одноэлементное перечисление
Самый простой способ реализовать синглтон, который является потокобезопасным, - использовать Enum
Этот код работает с момента появления Enum в Java 1.5.
Двойная проверка блокировки
Если вы хотите создать «классический» синглтон, работающий в многопоточной среде (начиная с Java 1.5), вам следует использовать этот.
До 1.5 это не было поточно-ориентированным, поскольку реализация ключевого слова volatile была другой.
Ранняя загрузка Singleton (работает даже до Java 1.5)
Эта реализация создает экземпляр singleton при загрузке класса и обеспечивает безопасность потоков.
источник
Вы также можете использовать статический блок кода для создания экземпляра при загрузке класса и предотвращения проблем синхронизации потоков.
источник
instance
финал 2. СделатьgetInstance()
статику.Обратитесь к этому сообщению, чтобы узнать, как лучше всего реализовать Singleton.
Как эффективно реализовать одноэлементный шаблон в Java?
Это зависит от того, как вы реализовали метод. Если вы используете двойную блокировку без изменчивой переменной, вы можете получить частично сконструированный объект Singleton.
Обратитесь к этому вопросу для получения более подробной информации:
Почему в этом примере блокировки с двойной проверкой используется volatile
Не требуется, если вы реализуете синглтон следующими способами
Обратитесь к этому вопросу для получения более подробной информации
Шаблон проектирования Java Singleton: вопросы
источник
Источник: Эффективная Java -> Пункт 2
Он предлагает использовать его, если вы уверены, что класс всегда останется singleton.
источник