Зачем использовать волатильный с синхронизированным блоком?
-
12-12-2019 - |
Вопрос
Я видел некоторые примеры в Java, где они делают синхронизацию на блоке кода, чтобы изменить некоторую переменную, в то время как эта переменная была объявлена летучим изначально.Сихронизированный блок, который инициализирует этот экземпляр ... Мой вопрос в том, почему мы объявляем его волатильным, пока мы синхронизируем на нем, почему нам нужно сделать обоими ??Разве один из них недостаточно для другого ??
public class someClass {
volatile static uniqueInstance = null;
public static someClass getInstance() {
if(uniqueInstance == null) {
synchronized(someClass.class) {
if(uniqueInstance == null) {
uniqueInstance = new someClass();
}
}
}
return uniqueInstance;
}
.
Спасибо заранее.
Решение
Синхронизация само по себе будет достаточно в этом случае, если первая проверка была в синхронизированном блоке (но это не так, и один поток не может видеть изменения, выполненные другим, если переменная не была волатильной).Нестабильный один не будет достаточно, потому что вам нужно выполнить более одной операции атомным образом.Но остерегайтесь!То, что у вас здесь, так называемая двойная проверка - обычная идиома, к сожалению, не работает надежно .Я думаю, что это изменилось с момента Java 1.6, но все же такой код может быть рискованным.
Другие советы
Это использует двойную проверку заблокировки, обратите внимание, что if(uniqueInstance == null)
не находится в синхронизированной части.
Если uniqueInstance
не является летучим, он может быть «инициализирован» с частично построенным объектом, в котором части его не видны, кроме потока, выполняемого в блоке synchronized
.Волатиль делает это все или ничего не работает в этом случае.
Если у вас не было синхронизированного блока, вы можете в конечном итоге с двумя потоками, получающимися в этот момент одновременно.
if(uniqueInstance == null) {
uniqueInstance = new someClass(); <---- here
.
и вы строите 2 объекта Someclass, который поражает цель.
Строго говоря, вам не нужен волатильный, метод мог быть
public static someClass getInstance() {
synchronized(FullDictionary.class) {
if(uniqueInstance == null) {
uniqueInstance = new someClass();
}
return uniqueInstance;
}
}
.
Но это вставляет синхронизацию и сериализацию каждого потока, которая выполняет getInstance ().
Это сообщение объясняет идеюза волатильным.
Он также обращается в семанную работу, Java Consullence на практике ,
Основная идея заключается в том, что параллелизм не только включает в себя защиту общего состояния, но и на видимость этого состояния между потоками: именно здесь воет волатильный. (Этот больший договор определяется
Вы можете сделать синхронизацию без использования синхронизированного блока. Не нужно использовать волатильную переменную в нем ... Волатильный обновляет одну переменную от основной памяти .. и Синхронизированное обновление Все общие переменные, доступные из основной памяти .. Так что вы можете использовать его в соответствии с вашими требованиями ..
Мои два цента здесь
Фрист Быстрое объяснение интуиции этого кода
if(uniqueInstance == null) {
synchronized(someClass.class) {
if(uniqueInstance == null) {
uniqueInstance = new someClass();
}
}
}
.
Причина, по которой она проверяет UniqueinStance== NULL дважды, - снизить накладные расходы на вызов синхронизированного блока, который относительно медленнее.Так называется двойной проверкой.
Во-вторых, причина, по которой она использует синхронизированную, легко понять, он делает две операции внутри синхронизированного блока атомной связи.
Последний летучий модификатор гарантирует, что все темы видят одну и ту же копию, поэтому самая первая проверка за пределами синхронизированного блока увидит значение UnaliveStance таким образом, что «синхронизировано» с синхронизированным блоком.Без летучих модификатора один нить может назначить значение для UnaliveNStance, но другой поток может не увидеть его по первой проверке.(Хотя второй чек увидит это)