Java Синглтон и синхронизация


пожалуйста, уточните мои вопросы, касающиеся Синглтона и многопоточности:

  • каков наилучший способ реализации синглтона в Java, в многопоточном режиме окружающая среда?
  • что происходит, когда несколько потоков пытаются получить доступ к getInstance() метод в то же время?
  • можем ли мы сделать синглтона getInstance()synchronized?
  • действительно ли необходима синхронизация при использовании одноэлементных классов?
8 97

8 ответов:

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

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

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 будет создан, и так как это контролируется ClassLoaders, нет необходима дополнительная синхронизация.

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

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, но я нахожу шаблон перечисления злоупотреблением концепцией перечисления (хотя это и работает)

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

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

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

Singleton.SINGLE.myMethod();

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

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

есть несколько способов сделать синглеты потокобезопасными, но сделать getInstance() synchronized, вероятно, самый простой.

перечисление синглтон

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

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

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

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

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

ранняя загрузка Синглтона (работает даже до 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");
  }

}

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

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}

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

обратитесь к этому сообщению для лучшего способа реализации Синглтона.

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

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

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

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

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

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

действительно ли необходима синхронизация при использовании одноэлементных классов?

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

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

см. этот вопрос для более подробной информации

Шаблон Проектирования Java Singleton: Вопросы

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

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

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