Java Singleton и синхронизация

Просьба уточнить мои запросы относительно Singleton и Multithreading:

  • Каков наилучший способ реализации Singleton в Java в многопоточной среде?
  • Что происходит, когда несколько streamов пытаются получить getInstance() методу getInstance() в одно и то же время?
  • Можем ли мы сделать synchronized getInstance() синглтона?
  • Необходима ли синхронизация при использовании classов Singleton?

Да, это необходимо. Существует несколько методов, которые можно использовать для обеспечения безопасности streamов с ленивой инициализацией:

Драконовская синхронизация:

 private static YourObject instance; public static synchronized YourObject getInstance() { if (instance == null) { instance = new YourObject(); } return instance; } 

Это решение требует, чтобы каждый stream был синхронизирован, когда на самом деле должны быть только первые несколько.

Синхронизация с двойным проверкой :

 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; } 

Это решение гарантирует, что только первые несколько streamов, которые пытаются приобрести ваш синглтон, должны пройти процесс приобретения блокировки.

Инициализация по требованию :

 private static class InstanceHolder { private static final YourObject instance = new YourObject(); } public static YourObject getInstance() { return InstanceHolder.instance; } 

Это решение использует преимущества модели памяти Java в отношении инициализации classа для обеспечения безопасности streamов. Каждый class может быть загружен только один раз, и он будет загружен только тогда, когда это необходимо. Это означает, что при первом вызове getInstance будет загружен instance InstanceHolder , и instance будет создан, и поскольку это контролируется ClassLoader s, дополнительная синхронизация не требуется.

Этот шаблон выполняет поточную инициализацию экземпляра без явной синхронизации!

 public class MySingleton { private static class Loader { static final MySingleton INSTANCE = new MySingleton(); } private MySingleton () {} public static MySingleton getInstance() { return Loader.INSTANCE; } } 

Он работает, потому что он использует загрузчик classов для выполнения всей синхронизации для вас бесплатно: class MySingleton.Loader впервые доступен внутри метода getInstance() , поэтому class Loader загружается, когда getInstance() вызывается в первый раз. Кроме того, загрузчик classов гарантирует, что вся статическая инициализация будет завершена, прежде чем вы получите доступ к classу – вот что дает вам безопасность streamов.

Это как волшебство.

Это на самом деле очень похоже на шаблон enums Jhurtado, но я нахожу шаблон enums злоупотреблением концепцией enum (хотя он действительно работает)

Если вы работаете над многопоточной средой на Java и должны гарантировать, что все эти streamи обращаются к одному экземпляру classа, вы можете использовать Enum. Это будет иметь дополнительное преимущество, помогая вам справляться с сериализацией.

 public enum Singleton { SINGLE; public void myMethod(){ } } 

и тогда просто ваши streamи будут использовать ваш экземпляр, например:

 Singleton.SINGLE.myMethod(); 

Да, вам нужно сделать getInstance() синхронизированным. Если это не так, может возникнуть ситуация, когда могут быть сделаны несколько экземпляров classа.

Рассмотрим случай, когда у вас есть два streamа, которые одновременно называют getInstance() . Теперь представьте, что T1 выполняет только мимо instance == null check, а затем запускает T2. На данный момент экземпляр не создается или не устанавливается, поэтому T2 передаст проверку и создаст экземпляр. Теперь представьте, что выполнение переключается обратно на T1. Теперь синглтон создан, но T1 уже проверил! Он снова начнет делать объект! Синхронизация getInstance() предотвращает эту проблему.

Есть несколько способов сделать streamи однопользовательскими, но сделать синхронизацию getInstance() вероятно, проще всего.

Enum singleton

Самый простой способ реализовать Singleton, который является streamобезопасным, – использовать Enum

 public enum SingletonEnum { INSTANCE; public void doSomething(){ System.out.println("This is a singleton"); } } 

Этот код работает с момента появления Enum в Java 1.5

Двойная проверка блокировки

Если вы хотите закодировать «classический» синглтон, который работает в многопоточной среде (начиная с Java 1.5), вы должны использовать эту.

 public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) { instance = new Singleton(); } } } return instance ; } } 

Это не является streamобезопасным до 1,5, поскольку реализация ключевого слова volatile отличается.

Ранняя загрузка Singleton (работает даже до Java 1.5)

Эта реализация создает экземпляр singleton при загрузке classа и обеспечивает безопасность streamов.

 public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public void doSomething(){ System.out.println("This is a singleton"); } } 

Вы также можете использовать статический блок кода для создания экземпляра экземпляра при загрузке classа и предотвращения проблем синхронизации streamов.

 public class MySingleton { private static final MySingleton instance; static { instance = new MySingleton(); } private MySingleton() { } public static MySingleton getInstance() { return instance; } } 

Каков наилучший способ реализации Singleton в Java в многопоточной среде?

Обратитесь к этому сообщению за лучший способ реализации Singleton.

Каков эффективный способ реализации одноэлементного шаблона в Java?

Что происходит, когда несколько streamов пытаются получить доступ к методу getInstance () в одно и то же время?

Это зависит от способа реализации метода. Если вы используете двойную блокировку без изменчивой переменной, вы можете получить частично построенный объект Singleton.

См. Этот вопрос для получения дополнительной информации:

Почему летучесть используется в этом примере двойной проверки блокировки

Можем ли мы сделать синхронизацию getInstance () синглтона?

Необходима ли синхронизация при использовании classов Singleton?

Не требуется, если вы реализуете Singleton ниже

  1. статическая интегрализация
  2. перечисление
  3. LazyInitalaization с Initialization-on-demand_holder_idiom

Подробнее см. Этот вопрос.

Шаблон дизайна Java Singleton: вопросы

 public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis () {...} } 

Источник: Эффективная Java -> Пункт 2

Он предлагает использовать его, если вы уверены, что class всегда будет оставаться одиночным.

  • В чем разница между синхронизацией на lockObject и использованием этого как блокировки?
  • Коллекции. Синхронизированный список и синхронизация
  • Символические ссылки и синхронизированные папки в Vagrant
  • Wait Firebase async извлекает данные на Android
  • Как работает блокировка?
  • Зачем использовать ReentrantLock, если вы можете использовать синхронизированный (это)?
  • Каковы различия между различными параметрами синхронизации streamов в C #?
  • Различия между неявным, явным и текучим
  • Убедитесь, что синхронизированные блокировки Java выполнены в порядке?
  • Синхронизировано ли унаследовано в Java?
  • Можно ли использовать мьютекс в многопроцессорном случае в Linux / UNIX?
  • Давайте будем гением компьютера.