2012-06-23 3 views
89

Просьба разъяснить мои вопросы относительно Singleton и многопоточности:Java Singleton и синхронизация

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

Имейте в виду, что Singleton - это анти-шаблон: http://www.yegor256.com/2016/06/27/singletons-must-die.html – yegor256

ответ

171

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

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

private static YourObject instance; 

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

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

Double check synchronization:

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

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

Initialization on Demand:

private static class InstanceHolder { 
    private static final YourObject instance = new YourObject(); 
} 

public static YourObject getInstance() { 
    return InstanceHolder.instance; 
} 

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

+17

Внимание! Будьте внимательны при синхронизации с двойной проверкой. Он не работает должным образом с JVM до Java 5 из-за «проблем» с моделью памяти. –

+3

-1 'Драконовская синхронизация' и' Двойная проверка синхронизации' getInstance() - метод должен быть статическим! –

+2

@PeterRader Они не нуждаются в 'статике ', но это может иметь больше смысла, если бы они были. Исправлено по запросу. – Jeffrey

8

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

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

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

+0

Будет ли это помогать, поместив код создания объекта в синхронизированный блок, вместо полной синхронизации метода? – RickDavis

+0

@ RaoG Нет. Вы хотите как проверку _and_ создания в блоке синхронизации. Вам нужны эти две операции, которые происходят вместе без перерывов или ситуация, описанная выше, может произойти. – Oleksi

20

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

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

, а затем просто ваши потоки используют свой экземпляр, как:

Singleton.SINGLE.myMethod(); 
+1

Подход Enum будет работать только для JDK1.5 или выше. – realPK

53

Эта модель делает потокобезопасного отложенной инициализации экземпляра без явной синхронизации!

public class MySingleton { 

    private static class Loader { 
     static final MySingleton INSTANCE = new MySingleton(); 
    } 

    private MySingleton() {} 

    public static MySingleton getInstance() { 
     return Loader.INSTANCE; 
    } 
} 

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

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

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

+6

Синхронизация по-прежнему присутствует, она просто выполняется JVM, а не программистом. – Jeffrey

+0

@ Джеффри. Вы правы, конечно. Я печатал все это (см. Правки). – Bohemian

+0

+1 очень хороший ответ. – zencv

2

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

public class MySingleton { 

    private static final MySingleton instance; 

    static { 
    instance = new MySingleton(); 
    } 

    private MySingleton() { 
    } 

    public static MySingleton getInstance() { 
    return instance; 
    } 

} 
+0

Что такое MySingletone? –

+0

Это была опечатка. Изменено – usha

+0

@Vimsha Еще пара вещей. 1. Вы должны сделать «экземпляр» final 2. Вы должны сделать 'getInstance()' static. –

5

Enum одноэлементный

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

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

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

Замок с двойной проверкой

Если вы хотите ввести "классический" singleton, который работает в многопоточной среде (начиная с 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 ; 
    } 
} 

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

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

Эта реализация инстанцирует одноплодной при загрузке класса и обеспечивает безопасность потока.

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

} 
0

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

См. Это сообщение, чтобы лучше всего реализовать Singleton.

What is an efficient way to implement a singleton pattern in Java?

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

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

Обратитесь к этому вопросу для более подробной информации:

Why is volatile used in this example of double checked locking

Можем ли мы сделать Синглтон деЫпзЬапсе() синхронизируется?

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

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

  1. статический intitalization
  2. перечисление
  3. LazyInitalaization с Initialization-на-demand_holder_idiom

Обратитесь к этому вопросу передний более подробности

Java Singleton Design Pattern : Questions

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

Источник: Effective Java -> Пункт 2

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

Смежные вопросы