Насколько известно, изменив volatile
переменную, мы остаёмся уверенны, что остальные потоки, которые будут её читать, получат новое значение. Причина тому ясна: значения переменных не кэшируются (конечно, там больше происходящего, но сейчас важна суть). Так вот мне стало неясно, а что происходит с видимостью изменений non-volatile
переменных при синхронизации?
В достоверном источнике сказано следующее:
Синхронизация вынуждает запись данных происходить в основную память, и если поле полностью защищено синхронизированными методами или блоками, объявлять его volatile
для параллельного доступа не обязательно.
Так вот, вопрос: а как это случается, почему же ключевое слово volatile
нам теперь не нужно? Нам нужны synchronized
getter'ы и setter'ы, али же достаточно синхронизировано прочитать? А может только записать?
Поясню часть вопроса про getter'ы и setter'ы:
Пусть есть код:
class A {
int n=1;
public void change(){
while (!Thread.interrupted())
synchronized (this){
n=3*n+4;
}
}
}
в main(String[] args)
того же пакета, что и класс A
:
A a = new A();
new Thread(a::change).start();
TimeUnit.SECONDS.sleep(10);
System.out.println(a.n);
И здесь возникает вопрос.
Давайте сейчас забудем про то, что переменная n
могла переполниться, а потом через наш sleep в 10 секунд снова стать равной 1. И забудем про то, что когда мы читаем n
она может быть в некотором "неустойчивом" состоянии.
Так вот, достаточно ли было синхронизации в методе change()
? Гарантирует ли она проталкивание в main memory
нашей переменной n
? Т.е. по факту вопрос такой: правда ли, что не будет такого, что через 10 секунд сна доставая a.n
мы получим там 1, ибо значение этого поля пока лежит в кэше?
А если изменить последнюю строчку в main(String[] args)
на:
synchronized (a){
System.out.println(a.n);
}
Теперь точно всё хорошо и проблем с видимостью изменений не будет? Как так устроен этот аспект механизма синхронизации? Т.е. change()
не проталкивал в main memory
, а при синхронизированном чтении сразу проталкивалось всё?... Или синхронизации в change()
было достаточно?
В приложение к ответу буду очень благодарен увидеть цитаты из документации (с переводом на русский язык) к Java с ссылкой на них.
Здеся и тута есть важные мысли, но я хочу получить ответ на всё-таки другой вопрос.
Модификаторы volatile
и synchronized
решают ведь не только проблему с кэшированием. Есть ещё масса нюансов, которые требуется учитывать в многопоточном программировании. Например, reordering. Есть ряд случаев, в которых доступ к переменным (полям объектов, статическим полям и элементам массива) может выполниться в порядке, отличном от указанного в программе. Компилятор свободен в расположении инструкций с целью оптимизации. Процессоры могут выполнять инструкции в ином порядке в ряде случаев. Данные могут перемещаться между регистрами, кэшами процессора и оперативной памятью в порядке, отличном от указанного в программе. Но JMM гарантирует сохранение отношений happens-before внутри синхронизированных блоков, устанавливая барьер при захвате и освобождении блокировки. Сама семантика слова "синхронизация" говорит за себя. Поток осуществляющий синхронизированный доступ к переменной, синхронизирует своё состояние. Но действительно актуальное состояние переменной для всех возможно только в том случае, если синхронизируются все.
Кроме того, не стоит забывать, что синхронизация используется не только для решения проблемы видимости, но и атомарности. Если один поток захватывает блокировку при доступе к полю, а другой даже не пытается её захватить и, соответственно, проверить её состояние, то толку от блокировки нет, и программа испортит состояние.
P.S. Вместо ссылок на документацию приведу ссылки на доклады человека, чьё имя стало нарицательным в этой области.
Aleksey Shipilёv - Java Memory Model Pragmatics, part 1
Aleksey Shipilёv - Java Memory Model Pragmatics, part 2
Алексей Шипилёв — Близкие Контакты JMM-степени
Виртуальный выделенный сервер (VDS) становится отличным выбором
файл сохраняется но он пустойпри дебаге выяснилось что output пустой
Пишу проекты на ReactПериодически требуется рендерить для ботов SPA на сервере
Как подключить таблицу стилей из другой папки ? Уже все перепробовал и так import "/сss/bootstrap