#java #multithreading
#java #многопоточность
Вопрос:
У меня есть класс со статическим переменным, например
private static Object sMyStaticVar;
если я хочу присвоить значение этому параметру в конструкторе, у меня есть код, подобный
if(sMyStaticVar == null) sMyStaticVar = new CustomObject(someRuntimeObject);
где someRuntimeObject
находится объект, который недоступен во время загрузки моего класса и, следовательно, не позволяет мне объявлять мой статический var, как показано ниже
private static Object sMyStaticVar = new CustomObject(someRuntimeObject);
мой вопрос в том, безопасна ли инициализация статического объекта var в потоке конструктора? Мои инстинкты говорят мне, что это не так, и я должен синхронизировать, используя тип класса, не относящийся к среде выполнения, в качестве блокировки, как показано ниже
synchronized(MyClass.class)
{
if(sMyStaticVar == null) sMyStaticVar = new CustomObject(someRuntimeObject);
}
(в отличие от типа среды выполнения, полученного из getClass()
)
но поскольку мои инстинкты обычно ошибочны, я был бы признателен, если бы кто-нибудь мог пролить некоторый свет на это для меня!
Комментарии:
1. (Изменяемая статика — действительно плохая идея.)
2. как так? в этом случае я только создаю объект и в любом случае присваиваю его статической переменной.
Ответ №1:
Если он статический, вы не должны назначать его в конструкторе. Создайте статический метод инициализатора, который это делает public static synchronized void initialize(someRuntimeObject)
.
Обратите внимание на synchronized
ключевое слово: это то же самое, что синхронизация на MyClass.class
Комментарии:
1. и вызвать этот метод из конструктора? Будет ли это фактически таким же, как мое предложение выше, но немного аккуратнее?
2. нет, не вызывайте это в конструкторе. Вызовите его из любого места, где вы бы вызывали конструктор. Конструктор предназначен для каждого объекта, а ваше поле
static
— для каждого класса.3. вы хотите сказать, что я должен вызывать его одновременно с созданием объекта? Почему это не сработало бы, если бы я вызвал его из конструктора?
4. Я не хочу каждый раз вызывать этот метод инициализации перед созданием нового объекта такого типа.
5. @Dori как я уже отмечал,
static
поля для каждого класса. Это не имеет никакого отношения к конструктору. Вы не должны смешивать эти две вещи. Либо сделайте его нестатическим и инициализируйте его в конструкторе, либо сделайте его статическим и инициализируйте его один раз
Ответ №2:
Вы правы, следующее открыто для условий гонки:
if(sMyStaticVar == null) sMyStaticVar = new CustomObject(someRuntimeObject);
Два потока могут sMyStaticVar
одновременно проверять, просматривать null
, создавать два объекта и т. Д…
Это означает, что вам нужна синхронизация. Вы можете либо выполнить синхронизацию с каким-либо существующим объектом (есть несколько вариантов), либо вы можете создать объект только для этой цели, чтобы вам не приходилось делиться блокировкой с кем-либо еще, рискуя ненужным конфликтом:
private static Object sMyStaticVar;
private static Object sMyStaticVarLock = new Object();
Затем в конструкторе:
synchronized(sMyStaticVarLock)
{
if(sMyStaticVar == null) sMyStaticVar = new CustomObject(someRuntimeObject);
}
Комментарии:
1. будет ли синхронизация с блокировкой, которую вы описали выше, иметь какое-либо преимущество перед synchronized (MyClass.class )?
2. @Dori: Если вы не знаете наверняка, кто еще может синхронизировать
MyClass.class
и с какой целью, вы не можете знать, на какую блокировку вы подписываетесь. Здесь вы точно знаете, что блокировка используется ровно для одной цели.
Ответ №3:
Эта синхронизация необходима, но этого недостаточно для обеспечения потокобезопасности.
Вам также необходимо обеспечить видимость значения поля при обращении к нему. Итак, вы должны либо объявить это поле как volatile
, либо добавить ту же синхронизацию для каждого доступа к этому полю.