правильное использование синхронизированного синглтона? - PullRequest
10 голосов
/ 22 апреля 2009

Так что я подумываю о создании хобби-проекта, единственного в своем роде, просто чтобы освежить в памяти мое программирование / дизайн.

Это в основном многопоточный веб-паук, обновляющий тот же объект структуры данных-> int.

Так что использование базы данных для этого определенно излишне, и единственное, о чем я могу подумать, - это потокобезопасный синглтон, используемый для хранения моей структуры данных. http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html

Есть ли другой подход, на который я должен обратить внимание?

Ответы [ 10 ]

30 голосов
/ 22 апреля 2009

Двойная проверка блокировки была доказана, чтобы быть неправильной и ошибочной (как минимум в Java). Сделайте поиск или посмотрите на запись в Википедии по точной причине.

Прежде всего, это правильность программы. Если ваш код не является поточно-ориентированным (в многопоточной среде), то он сломан. Корректность важнее, чем оптимизация производительности.

Чтобы быть точным, вам нужно синхронизировать весь метод getInstance

public static synchronized Singleton getInstance() {
   if (instance==null) ...
}

или статически инициализировать его

private static final Singleton INSTANCE = new Singleton();
10 голосов
/ 22 апреля 2009

Использование отложенной инициализации для базы данных в веб-сканере, вероятно, не стоит. Ленивая инициализация добавляет сложность и постоянный удар по скорости. Один случай, когда это оправдано, - когда есть большая вероятность, что данные никогда не понадобятся. Кроме того, в интерактивном приложении его можно использовать для сокращения времени запуска и обеспечения иллюзии скорости.

Для неинтерактивного приложения, такого как веб-сканер, которому, безусловно, понадобится база данных, существует ленивая инициализация.

С другой стороны, веб-сканер легко распараллеливается и значительно выиграет от многопоточности. Использовать его как упражнение для освоения библиотеки java.util.concurrent было бы крайне полезно. В частности, посмотрите ConcurrentHashMap и ConcurrentSkipListMap, , которые позволят нескольким потокам читать и обновлять общую карту.

Когда вы избавляетесь от отложенной инициализации, самый простой шаблон Singleton выглядит примерно так:

class Singleton {

  static final Singleton INSTANCE = new Singleton();

  private Singleton() { }

  ...

}

Ключевое слово final является ключом здесь. Даже если вы предоставляете static «метод получения» для синглтона вместо прямого доступа к полю, создание синглтона final помогает обеспечить корректность и позволяет более агрессивную оптимизацию компилятором JIT.

2 голосов
/ 07 июля 2010

, как утверждает Джошуа Блох в своей книге «Эффективное Java-издание 2-е», я также согласен с тем, что одноэлементный тип перечисления является лучшим способом реализации синглтона.

public enum Singleton {
  INSTANCE;

  public void doSomething() { ... }
}
2 голосов
/ 23 апреля 2009

Попробуйте Bill Pugh решение инициализации по требованию держателя. Решение является наиболее переносимым среди различных компиляторов Java и виртуальных машин. Решение является поточно-ориентированным и не требует специальных языковых конструкций (т. Е. Volatile и / или синхронизированных).

http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh

2 голосов
/ 22 апреля 2009

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

Но в данном случае ключевое слово здесь - проект хобби !

Это означает, что если вы синхронизировали весь метод getInstance () , с вами все будет в порядке в 99,9% всех случаев. Я бы НЕ рекомендовал делать это другим способом.

Позже, если вы с помощью профилирования докажете, что синхронизация getInstance () является узким местом вашего проекта, тогда вы можете двигаться дальше и оптимизировать параллелизм. Но я действительно сомневаюсь, что это доставит вам неприятности.

Jeach!

1 голос
/ 23 апреля 2009

Почему бы вам не создать структуру данных, которую вы передаете каждому из потоков как внедрение зависимостей. Таким образом, вам не нужен синглтон. Вам все еще нужно сделать поток безопасным.

1 голос
/ 22 апреля 2009

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

class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();

  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}

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

Все, что сказано, возможно, что вам действительно нужно, это одна из многопоточных структур данных, доступных в современных JDK. Например, я большой поклонник ConcurrentHashMap : безопасность потоков плюс мне не нужно писать код (FTW!).

0 голосов
/ 22 апреля 2009

Как насчет:

public static Singleton getInstance() {
  if (instance == null) {
    synchronize(Singleton.class) {
      if (instance == null) {
         instance = new Singleton();
      }
    }
  }

  return instance;
}
0 голосов
/ 22 апреля 2009

Ознакомьтесь с этой статьей Реализация шаблона Singleton в C #

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}
0 голосов
/ 22 апреля 2009

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

...