Насколько известно, изменив 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 с ссылкой на них.
Здеся и тута есть важные мысли, но я хочу получить ответ на всё-таки другой вопрос.
если блокировка сейчас у P, и вычисления физики долгие- не нужно захватывать блокировку на все вычисление, она нужно только при записи результатова в разделяемые переменные. Более того тут достаточно только volatile (и при записи и при чтении), с ним синхронизация быстрее, так так нужно синхронизировать только в одну сторону (при записи от записывающего в память, а при считывании из памяти к читающему), т.е делать половину работы. Плюс volatile не блокирует ни читателя ни писателя в отличии от synchronized. Так что М может себе читать когда угодно без проблем. – Roman-Stop RU aggression in UA Jun 12 '22 at 06:25