JavaRush /Курсы /Java Multithreading /Применение volatile

Применение volatile

Java Multithreading
6 уровень , 4 лекция
Открыта

— Привет, Амиго!

— Привет, Элли!

— Хочу рассказать тебе о модификаторе volatile. Знаешь, что это такое?

— Вроде что-то связанное с нитями. Не помню точно.

— Тогда слушай. Вот тебе немного технических деталей:

В компьютере есть два вида памяти – глобальная (обычная) и встроенная в процессор. Встроенная в процессор делится на регистры, затем кэш первого уровня (L1), кэш второго уровня (L2) и третьего уровня (L3).

Эти виды памяти отличаются по скорости работы. Самая быстрая и самая маленькая память – это регистры, затем идет кэш процессора (L1, L2, L3) и, наконец, глобальная память (самая медленная).

Скорость работы глобальной памяти и кэша процессора сильно отличаются, поэтому Java-машина позволяет каждой нити хранить самые часто используемые переменные в локальной памяти нити (в кэше процессора).

— А можно как-то управлять этим процессом?

— Практически никак – всю работу делает Java-машина, она очень интеллектуальная в плане оптимизации скорости работы.

Но я тебе это рассказываю вот зачем. Есть одна маленькая проблемка. Когда две нити работают с одной и той же переменной, каждая из них может сохранить ее копию в своем внутреннем локальном кэше. И тогда может получится такая ситуация, что одна нить переменную меняет, а вторая не видит этого изменения, т.к. по-прежнему работает со своей копией переменной.

— И что же делать?

— На этот случай разработчики Java предусмотрели специальное ключевое слово – volatile. Если есть переменная, к которой обращаются из разных нитей, ее нужно пометить модификатором volatile, чтобы Java-машина не помещала ее в кэш. Вот как это обычно выглядит:

public volatile int count = 0;

— О, вспомнил. Ты же уже про это рассказывала. Я же это уже знаю.

— Ага, знаешь. А вспомнил, только когда я рассказала.

— Э, ну забыл немного.

— Повторение – мать учения.

Вот тебе несколько новых фактов работы модификатора volatile. Модификатор volatile гарантирует только безопасное чтение/запись переменной, но не ее изменение.

— А в чем разница?

— Вот смотри. Как изменяется переменная:

Код Что происходит на самом деле: Описание
count++
register = count;

register = register+1;

count = register;
Этап 1.
Значение переменной count копируется из глобальной памяти в регистровую память процессора.

Этап 2
Внутри процессора регистровая переменная увеличивается на 1.

Этап 3
Значение переменной копируется из процессора в глобальную память.

— Ого! Так что, изменение любой переменной происходит только в процессоре?

— Ага.

— И значения копируются туда-сюда: из памяти в процессор и обратно?

— Ага.

Так вот, модификатор volatile, гарантирует, что при обращении к переменной count она будет прочитана из памяти (этап 1). А если какая-то нить захочет присвоить ей новое значение, то оно обязательно окажется в глобальной памяти (этап 3).

Но Java-машина не гарантирует, что не будет переключения нитей между этапами 1 и 3.

— Т.е. увеличение переменной на 1 – это фактически три операции?

— Да.

— И если две нити одновременно захотят исполнить count++, то они могут помешать друг другу?

— Да, вот смотри:

Нить 1 Нить 2 Результат
register1 = count; register1++; count = register1;
register2 = count; register2++; count = register2;
register1 = count;
register2 = count; register2++; count = register2;
register1++; count = register1;

— Т.е. обращаться к переменной можно, а изменять рискованно все равно?

— Ну, изменять можно, только осторожно ☺

— Это как же?

— synchronized наше все.

— Ясно.

Комментарии (102)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
I'll kick them all Уровень 5
24 августа 2025
— Я – робот, у меня не бывает шишек – только вмятины. — Ну, значит, набить вмятин. ... — О, вспомнил. Ты же уже про это рассказывала. Я же это уже знаю. — Ага, знаешь. А вспомнил, только когда я рассказала. — Э, ну забыл немного. У Амиго походу кеш почистился. И ключевое слово volatile не дошло до persistance.
G L Уровень 38
10 мая 2025
Как я понимаю в современной практике volatile подходит только для флагов. Когда один поток в цикле что-то делает под флагом пока true, а другой поток поменял флаг и тогда первый останавливается. В инных случаях Atomic-обертка, если логики программы достаточно или syncrhomized-блок, если нет.
Бромгексин Уровень 38
1 сентября 2024
кажется что не сильно то и помогает этот volatile, на уровне yield
SomeBody098 Уровень 51
29 июля 2024
Григорий Уровень 1
29 июля 2024
Есть очень хорошая хоть и сложная на мой взгляд книга Эндрю Таненбаума "Архитектура компьютера". Там на страницах 76-79 указана информация про тракт данных, про который в лекции неявно упомянули (наверно). Если я ошибаюсь в своих суждениях, то заранее прошу за это прощения!
Long_byte Уровень 54
10 июня 2024
лекция класс все понятно чтение и запись эти операции атомарные а операция инкремента здесь не атомарно значить операцию инкремента нужно синхронизировать
Anemon Уровень 13 Expert
18 декабря 2024
Или использовать атомарную операцию объекта вроде AtomicInteger, к примеру
Алексей Уровень 42
9 мая 2024
Процитирую Заура Трегулова: volitile подходит в тех случаях когда только один поток изменяет переменную, а остальные только читают
5 апреля 2024
Да жаль что в Java нет какого-нибудь специального пакета для работы в многопоточной среде, без забивания головы технологиями древних.
Suzuya Jūzō Уровень 46
24 апреля 2023
🔽здесь попонятнее будет объяснение🔽 Управление потоками. Ключевое слово volatile
Lafaed Уровень 36
10 апреля 2023
— synchronized наше все. — Ясно, что нихрена не ясно. Тоесть надо делать так ?

volatile int count = 5;
synchronized (count) {
    count++;
}
Или так ?

int count = 5;
synchronized (count) {
    count++;
}
Евгений Уровень 32 Expert
12 июня 2023
а как ты int собрался синхронизировать, а во втором случае у тебя переменная вообще локальная, у тебя каждый поток будет иметь свою копию этой переменной
Archy Уровень 1 Expert
14 сентября 2023
Переменные, использованные в синхронизированном блоке, уже volatile. Это из книги Джошуа Блоха