правильное использование синхронизированного синглтона?

StackOverflow https://stackoverflow.com/questions/777849

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Чтобы быть корректным, вам придется синхронизировать весь getInstance способ

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

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

private static final Singleton INSTANCE = new Singleton();

Другие советы

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

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

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

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

class Singleton {

  static final Singleton INSTANCE = new Singleton();

  private Singleton() { }

  ...

}

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

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

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

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

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

Джич!

Попробуйте Билл Пью решение проблемы инициализации по требованию владельца идиомы.Это решение является наиболее переносимым для различных компиляторов Java и виртуальных машин.Решение потокобезопасно, не требуя специальных языковых конструкций (т.е.изменчивый и /или синхронизированный).

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

как утверждает Джошуа Блох в своей книге "effective java 2nd edition", я также согласен с тем, что тип перечисления с одним элементом является лучшим способом реализации singleton.

public enum Singleton {
  INSTANCE;

  public void doSomething() { ... }
}

Если вы посмотрите в самый низ этой статьи, вы увидите предложение просто использовать статическое поле.Это было бы моим желанием:вам действительно не нужно ленивое создание экземпляра (так что вам не нужно 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;
  }
}

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

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

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

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

Ознакомьтесь с этой статьей Реализация одноэлементного шаблона в 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();
    }
}

Как насчет:

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

  return instance;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top