Как создать экземпляр объекта с безопасным потоком в сеансе? - PullRequest
1 голос
/ 30 октября 2019

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

В настоящее время я делаю что-то вроде этого;

  public final class ObjectFactory {

    private static volatile NativeObjectWrapper instance = null;

    private Singleton() {}

    public static NativeObjectWrapper getInstance() {
        if (instance == null) {
            synchronized(ObjectFactory.class) {
                if (instance == null) {
                    instance = new NativeObjectWrapper(AuthData);
                }
            }
        }

        return instance;
    }

    public void reset() {
      synchronized(ObjectFactory.class) {
        instance = null;
      }
    }
  }

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

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

Ответы [ 2 ]

0 голосов
/ 30 октября 2019

Является ли вышеуказанный подход безопасным для потоков?

Ответ зависит от того, что, по вашему мнению, означает "безопасный для потоков". В вашем методе reset() нет ничего, что препятствовало бы тому, чтобы поток, который ранее вызывал getInstance(), продолжал использовать старый экземпляр.

Является ли это "потокобезопасным?"

Вообще говоря, "Потокобезопасный »означает, что действия одного потока никогда не могут привести к тому, что другие потоки увидят общие данные в несогласованном или недопустимом состоянии. Но что означает «противоречивый» или «недействительный», зависит от структуры общих данных (т. Е. От дизайна приложения).


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

ПРИМЕЧАНИЕ. Это более слабое определение «безопасности потоков», поскольку оно закрывает тот факт, чтоиспользование поточно-ориентированных компонентов для построения системы не гарантирует, что сама система будет поточно-ориентированной.

Все ли, кто использует ваш класс ясно , понимают, что ни один поток в программе не можеткогда-либо звоните reset(), в то время как любая ссылка на старый синглтон все еще существует? Если так, то я бы назвал это слабым замыслом, потому что он очень далек от того, чтобы быть «безопасным для младших программистов», но я бы неохотно признал, что со строгой, языковой, юридической точки зрения вы могли бы назвать свой ObjectFactory класс " нить сейф."

0 голосов
/ 30 октября 2019

Является ли вышеуказанный подход безопасным?

Нет, это не так.

Скажем, у нас есть два потока - A и B.

A, звонки getInstance(), передачипроверка instance==null, а затем переключение контекста на B, которое вызывает reset(). После того, как B завершит выполнение reset(), A снова получит контекст и вернет instance, который теперь равен нулю.

, если нет, существует ли общий шаблон для решения этой проблемы?

Я не помню, чтобы видел синглтоны методом reset, поэтому мне не известны какие-либо общие шаблоны для этой проблемы. Однако самым простым решением было бы просто удалить первый if (instance == null) чек в getInstance(). Это сделает ваш поток реализации безопасным, так как instance всегда проверяется и изменяется в синхронизированном блоке. В этом сценарии вы также можете удалить модификатор volatile из instance, поскольку он всегда доступен из синхронизированного блока.

Есть более сложные решения, о которых я могу подумать, но я бы использовал ихтолько если реальное профилирование показало, что вы тратите слишком много времени на этот блок synchronized. Обратите внимание, что у JVM есть несколько изощренных способов избегать использования "настоящих" блокировок для минимизации блокирования.

Одним из хитрых подходов может быть чтение поля instance один раз:

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

    return toReturn ;
}

Но это может привести к возвращению старого "экземпляра". Например, поток может выполнить Singleton toReturn = instance и получить действительный экземпляр, а затем потерять ЦП. На этом этапе 1000 других потоков могут создавать и сбрасывать 1000 других экземпляров до тех пор, пока исходный поток снова не получит вращение ЦП, после чего он возвращает старое значение instance. Вам решать, является ли такой случай приемлемым.

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