вопрос о синглтон-классах и потоках - PullRequest
1 голос
/ 09 февраля 2011

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

 public static synchronized IndexUpdater getIndexUpdater() {
    if (ref == null)
        // it's ok, we can call this constructor
        ref = new IndexUpdater();
    return ref;
}

private static IndexUpdater ref;

Давайте предположим, что в классе есть другие методы, которые выполняют реальную работу (индикаторы обновления и т. Д.). Что я пытаюсь понять, так это то, как доступ и использование синглтона будут работать с двумя потоками. Давайте предположим, что во время 1 поток 1 получает ссылку на класс посредством вызова, подобного этому IndexUpdater iu = IndexUpdater.getIndexUpdater (); Затем, во время 2, используя ссылку iu, метод внутри класса называется потоком iu.updateIndex. Что произойдет во время 2, второй поток попытается получить ссылку на класс. Может ли это сделать, а также получить доступ к методам внутри синглтона или это будет предотвращено, если первый поток имеет активную ссылку на класс. Я предполагаю последнее (или иначе, как это будет работать?), Но я хотел бы убедиться, прежде чем я реализую.

Спасибо,

Эллиот

Ответы [ 8 ]

2 голосов
/ 09 февраля 2011

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

public class UnsafeSingleton {
    private static UnsafeSingleton singletonRef;
    private Queue<Object> objects = new LinkedList<Object>();

    public static synchronized UnsafeSingleton getInstance() {
        if (singletonRef == null) {
            singletonRef = new UnsafeSingleton();
        }

        return singletonRef;
    }

    public void put(Object o) {
        objects.add(o);
    }

    public Object get() {
        return objects.remove(o);
    }
}

Два потока, вызывающие getInstance, гарантированно получат один и тот же экземпляр UnsafeSingleton, поскольку синхронизация этого метода гарантирует, что singletonRef будет установлен только один раз.Однако возвращаемый экземпляр не является потокобезопасным , поскольку (в этом примере) LinkedList не является потокобезопасной очередью.Два потока, изменяющие эту очередь, могут привести к непредвиденному поведению.Необходимо предпринять дополнительные шаги, чтобы гарантировать, что сам синглтон является потокобезопасным, а не только его экземпляром.(В этом примере реализация очереди может быть заменена, например, LinkedBlockingQueue, или методы get и put могут быть помечены synchronized.)

2 голосов
/ 09 февраля 2011

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

Дополнительная информация о: http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

2 голосов
/ 09 февраля 2011

Ваше предположение неверно. Синхронизация getIndexUpdater () предотвращает создание более чем одного экземпляра разными потоками, вызывающими getIndexUpdater () (почти) одновременно.

Без синхронизации может произойти следующее: Поток один вызывает getIndexUpdater (). ref является нулем. Поток 2 вызывает getIndexUpdater (). ref все еще нулевой. Результат: ссылка создается дважды.

1 голос
/ 09 февраля 2011

Затем, во время 2, используя ссылку iu, метод в классе называется iu.updateIndex потоком 1. Что произойдет во время 2, второй поток попытается получить ссылку на класс. Может ли он сделать это, а также получить доступ к методам в пределах синглтона ...?

Ответ - да. Ваше предположение о том, как получены ссылки, неверно. Второй поток может получить ссылку на синглтон. Паттерн Синглтон чаще всего используется как своего рода псевдоглобальное состояние. Как мы все знаем, с глобальным состоянием обычно очень трудно иметь дело, когда его используют несколько объектов. Чтобы сделать ваш одноэлементный поток безопасным, вам необходимо использовать соответствующие механизмы безопасности, такие как использование атомарных классов-оболочек, таких как AtomicInteger или AtomicReference (и т. Д.) Или использование synchronize (или Lock) для защиты критических одновременное обращение к областям кода.

0 голосов
/ 09 февраля 2011

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

0 голосов
/ 09 февраля 2011

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

ИМХО самый простой способ реализовать синглтон - это иметь перечисление с одним значением

enum Singleton {
    INSTANCE
}

Это потокобезопасно и создает INSTANCE только при обращении к классу.

0 голосов
/ 09 февраля 2011

Когда второй поток пытается вызвать метод getIndexUpdater(), он попытается получить так называемый lock , созданный для вас при использовании ключевого слова synchronized. Но поскольку какой-то другой поток уже находится внутри метода, он получил блокировку раньше, а другие (например, второй поток) должны ждать ее.

Когда первый поток завершит свою работу, он снимет блокировку, а второй поток немедленно возьмет его и введет метод. Подводя итог, использование synchronized всегда позволяет только одному потоку войти в защищенный блок - очень ограниченный доступ.

0 голосов
/ 09 февраля 2011

Наиболее безопасным является использование enum-singleton.

public enum Singleton {
  INSTANCE;
  public String method1() {
    ...
  }
  public int method2() {
    ...
  }
}

Потоково-ориентированный, сериализуемый, загружаемый с отложенной загрузкой и т. Д. Только преимущества!

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