Насколько известно, изменив 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-степени
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
файл сохраняется но он пустойпри дебаге выяснилось что output пустой
Пишу проекты на ReactПериодически требуется рендерить для ботов SPA на сервере
Как подключить таблицу стилей из другой папки ? Уже все перепробовал и так import "/сss/bootstrap