Нужно синхронизировать потокобезопасные методы синглтона

1

Скажем, что у меня синглтон:

public class MySinglton {

    private static volatile MySinglton s;
    private int x; 

    public static MySinglton getInstance() {
        if (s != null) return s;
        synchronized (MySinglton.class) {
            if (s == null) {
                s = new MySinglton();
            }
        }
        return s;
    }

    public void setX(int x){
        this.x = x;
    }

} 

Хорошо, метод getInstance является потокобезопасным. Мой вопрос. Нужно ли изменять метод setX, или это потокобезопасно, потому что метод getInsatnce является потокобезопасным. Если это не то, что лучше.

    synchronized public void setX(int x){
        this.x = x;
    }

    public void setX(int x){
        synchronized (this) {
            this.x = x;
        }
    }

или, наконец,

    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void setX(int x){
        readWriteLock.readLock().lock();
        this.x = x;
        readWriteLock.readLock().unlock();
    }
Теги:
multithreading

6 ответов

2
Лучший ответ

Просто потому, что getInstance() является потокобезопасным, это не означает, что любые другие методы класса являются потокобезопасными. Несколько потоков могут выполнять операции с объектом singleton одновременно.

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

Хорошим подходом к написанию синглтона является использование переименования, поскольку это гарантирует, что только один экземпляр этого будет. Тем не менее, функции-члены перечисления все равно должны быть синхронизированы, если вы хотите, чтобы он был потокобезопасным. См. Страницу 311 "Эффективная Java" на примере: http://uet.vnu.edu.vn/~chauttm/e-books/java/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf

2

Нет setX не является потокобезопасным, так как может быть несколько потоков, имеющих ссылку на экземпляр singleton, и они могут попытаться выполнить setX api в том же экземпляре.

Вам нужно будет сделать setX threadsafe. И ваша реализация метода с синхронизированным ключевым словом будет работать.

Я бы не использовал read lock для чтенияWriteLock, поскольку я пишу ему значение. Также, что, если что-то происходит между вами, вы получаете блокировку и разблокировку? Вы когда-нибудь разблокируете и, следовательно, выйдете в тупик. Поэтому всегда используйте блокировку и разблокировку в блоке try/catch/finally, где вы разблокируете, наконец, блок.

1

Наличие singleton не препятствует одновременному вызову setX(). Поэтому вам определенно нужна синхронизация здесь.

И кажется неудобным забрать блокировку READ перед ПИСЬМОЙ. Точка: читатели (которые вызывают отсутствующий метод "getX()") нуждаются в чтении; при записи вы хотите блокировать WRITE!

Если быть точным: вам нужна блокировка ReadWrite, если обновляемое свойство не является "атомарным", а ваша программа "знает" разные роли "читателя" и "писателя".

Если есть только метод "setX()" (и нет читателей вокруг, тогда "синхронизированный" должен делать, в этом случае вам не нужна блокировка RW).

  • 0
    Если есть только метод setX и нет читателей, метод и поле следует удалить, поскольку они не служат какой-либо цели :)
  • 0
    В приведенном примере да. Но, возможно, этот кусок кода был просто сокращен с целью задать вопрос. Так что, да, если этот «настоящий» код, x и setX () могут исчезнуть. Но если эти вещи действительно необходимы, то требуется синхронизация.
Показать ещё 1 комментарий
0

Все, что вы нам показали, - это одноэлементный объект с методом setX(int x). Но метод setX (x) ничего не делает, кроме как установить приватную переменную, которую не видит другой класс.

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

"Безопасность потоков" заключается в том, чтобы убедиться, что ваша программа будет делать то, что вы хотите, независимо от того, как выполняются чередования потоков. Что вы хотите, чтобы ваша программа выполняла?

Иногда мы говорим о "потокобезопасном" классе. Например, люди говорят, что java.util.concurrent.ArrayBlockingQueue является "потокобезопасным". Но это означает, что потоки в вашей программе не могут помещать ArrayBlockingQueue в плохое внутреннее состояние. Это не означает, что очередь всегда будет содержать объекты, которые ваша программа хочет, чтобы они содержались, или в том порядке, в котором ваша программа хочет. Построение программы из "потокобезопасных" объектов не обеспечивает безопасность потока программ.

Какие еще методы вы используете в своем классе MySingleton? Как он должен использоваться?

0
public final class MySinglton 
{ 
   private final static MySinglton s; 
   private volatile int x; 

   static {
       s = new MySinglton();
   }

   private MySinglton() {}

   public static MySinglton getInstance() 
   {
       return s;
   }

   public void setX(int x)
   {
      this.x = x; 
   }

}

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

0

setX вызывается в экземпляре s. Таким образом, в JVM есть только один случай s в любой момент времени (bcos it a singleton). Если два потока simulataeneely называют setX, они могут оба оверить один и тот же x или шаг друг на друга.

  • Напр. Без синхронизации, если поток A & thread B обновляет x в тот же момент времени, другие потоки, обращающиеся к этим значениям, могут видеть разные значения для x.

setX не является неявным потокобезопасным.

Это полезно иметь

public void setX(int x){
    synchronized (this) {
        this.x = x;
    }
}

Вот аналогичная пост -узкоспециализированная синхронизация Java в методах getter/setter и singleton pattern класса

  • 0
    Наличие двух потоков, перезаписывающих один и тот же x, не является проблемой. Это ожидается, и синхронизация метода не изменит этого. Проблема отсутствия синхронизации метода состоит в том, что другие потоки, считывающие значение x, не гарантированно увидят самое новое значение.
  • 0
    привет JB, да, это своего рода то, что я пытался объяснить ... думаю, я мог бы сделать намного лучшую работу. Я обновлю ответ .. спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню