предисловие
Изменчивость является необходимой основой для Java-программистов, и это также тема, которую любят задавать интервьюеры. В этой статье я начну с вами путешествие по изучению изменчивости. Если что-то не так, пожалуйста, укажите на это. Давайте учиться у каждого другое~
- 1. Использование volatile
- 2. Роль volatile переменных
- 3. Модель памяти современного компьютера (модель компьютера, шина, протокол МЭСИ, технология сниффинга)
- 4. Модель памяти Java (JMM)
- 5. 3 особенности параллельного программирования (атомарность, видимость, упорядоченность, «происходит раньше», «как если бы последовательно», перестановка инструкций)
- 6. Основной принцип volatile (как обеспечить видимость, как обеспечить перестановку инструкций, барьер памяти)
- 7. Типичные сценарии volatile (флаг состояния, одноэлементный режим DCL)
- 8. изменчивые общие вопросы интервью и анализ ответов
- Общественный номер: маленький мальчик собирает улиток
гитхаб-адрес
1. Использование volatile
Ключевое слово volatile предоставляется виртуальной машиной Java.Самый легкий механизм синхронизации, который появляется как модификатор дляпеременная-модификатор, но локальные переменные сюда не включены. Давайте посмотрим на демо, код выглядит следующим образом:
/**
* @Author 捡田螺的小男孩
* @Date 2020/08/02
* @Desc volatile的可见性探索
*/
public class VolatileTest {
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread t1 = new Thread(task, "线程t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("开始通知线程停止");
task.stop = true; //修改stop变量值。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程t2");
t1.start(); //开启线程t1
t2.start(); //开启线程t2
Thread.sleep(1000);
}
}
class Task implements Runnable {
boolean stop = false;
int i = 0;
@Override
public void run() {
long s = System.currentTimeMillis();
while (!stop) {
i++;
}
System.out.println("线程退出" + (System.currentTimeMillis() - s));
}
}
результат операции: Можно обнаружить, что поток t2, хотя стоп установлен на true, но поток t1 до t2Стоп-переменная невидима, поэтому он продолжает работать в бесконечном цикле. Если переменная stop изменена с помощью volatile, поток t1 может быть остановлен, и результат выполнения будет следующим:
volatile boolean stop = false;
2. Роль переменных-модификаторов vlatile
Из приведенного выше примера мы можем обнаружить, что переменная stop после добавления изменяемой модификации показывает, что поток t1 останавливается. На самом деле роль vlatile такова:Гарантированная переменная видимость для всех потоков. Конечно, у vlatile есть и другой эффект:Отключить перестановку инструкций, но этоАтомарность не гарантируется.
Поэтому, когда интервьюер спрашивает васРоль или характеристики летучих, можно ответить так:
- Гарантированная переменная видимость для всех потоков;
- Отключить переупорядочивание инструкций
- Атомарность не гарантируется
3. Модель памяти современного компьютера (модель компьютера, протокол МЭСИ, технология сниффинга, шина)
Чтобы лучше понять volatile, давайте рассмотрим модель памяти компьютера и JMM (модель памяти Java)~
компьютерная модель
Когда компьютер выполняет программу, инструкции выполняются процессором ЦП, а обрабатываемые данные находятся в основной памяти.
Так как вычислительная скорость запоминающего устройства компьютера и процессора различается на несколько порядков, невозможно, чтобы ЦП каждый раз выполнял инструкцию, а затем ждал медленного доступа основной памяти к данным. Поэтому современные компьютерные системы добавляют слой высокоскоростного кэша (Cache), скорость чтения и записи которого близка к скорости обработки процессором, в качестве буфера между памятью и процессором.
В многопроцессорной системе каждый процессор имеет собственный кэш и использует одну и ту же основную память.абстрактная модель памяти компьютераследующее:
- Когда программа выполняется, данные, которые необходимо использовать, копируются из основной памяти в кэш.
- Когда процессор ЦП вычисляет, он считывает из своего кеша и записывает вычисленные данные в кеш.
- Когда работа программы завершается, кэшированные данные сбрасываются в основную память.
С развитием науки и техники для повышения эффективности кеш-память получила кеш-память первого уровня (L1), кеш-память второго уровня (L2) и даже кеш-память третьего уровня (L3);
Когда вычислительные задачи нескольких процессоров используют одну и ту же область оперативной памяти, это может привести кНесогласованные данные кешапроблема. Как решить эту проблему? Есть два варианта
- 1. Добавляя LOCK# к шине.
- 2. Через протокол Cache Coherence
автобус
Шина (Bus) - это общая коммуникационная магистраль, которая передает информацию между различными функциональными компонентами компьютера.Это жгут передачи, состоящий из проводов.По типу информации, передаваемой компьютером, шину компьютера можно разделить на шина данных, шина адреса и шина управления, которые используются для передачи данных, адреса данных и управляющих сигналов соответственно.
ЦП и другие функциональные компоненты взаимодействуют через шину.Если к шине добавлена блокировка LOCK#, другие ЦП не могут получить доступ к памяти, пока шина заблокирована.Таким образом,менее эффективны.
протокол МЭСИ
Для решения проблемы когерентности также можно использовать протокол когерентности кэша. То есть каждый процессор следует некоторым протоколам при доступе к кешу и работает по протоколу при чтении и записи, к таким протоколам относятся MSI, MESI (Illinois Protocol), MOSI, Synapse, Firefly и Dragon Protocol. Самый известный из них — протокол Intel MESI (Modified Exclusive Shared Or Invalid), основная идея которого заключается в следующем:
Когда ЦП записывает данные, если он обнаруживает, что рабочая переменная является общей переменной, то есть копия переменной существует в других ЦП, он посылает сигнал, чтобы уведомить другие ЦП о том, что строка кэша переменной становится недействительной, поэтому когда другие ЦП должны читать При извлечении этой переменной он обнаруживает, что строка кэша, которая кэширует переменную в своем собственном кэше, недействительна, тогда она будет повторно прочитана из памяти.
4 состояния (M, E, S, I) каждого тега строки кэша в ЦП, давайте посмотрим:
состояние кэша | описывать |
---|---|
М, модифицированный (модифицированный) | Строка кэша кэшируется только этим ЦП и отличается от значения основной памяти, она будет записана в память до того, как она будет прочитана другими ЦП, и будет установлена на Общий |
E, Эксклюзив (Эксклюзив) | Строка кэша кэшируется только ЦП и имеет то же значение, что и основная память.Она устанавливается как Общая при чтении другими ЦП и устанавливается как Модифицированная при записи другими ЦП. |
S, общий (общий) | Строка кэша может кэшироваться несколькими процессорами, и данные в каждом кэше совпадают с данными основной памяти. |
Я, недействительный (Недействительный) | Данные строки кэша недействительны и при необходимости должны быть перезагружены из основной памяти. |
Как реализован протокол MESI? Как сделать так, чтобы внутренний кеш текущего процессора, основная память и данные кеша других процессоров согласовались на шине?Многопроцессорный анализ шины
технология сниффинга
В многопроцессорных системах для обеспечения согласованности кэшей каждого процессора будет реализован протокол когерентности кэш-памяти.Каждый процессор проверяет, не истек ли срок действия его собственного значения кэш-памяти, анализируя данные, распространяемые по шине.Если процессор обнаруживает, что адрес памяти, соответствующий его строке кэша, был изменен, он установит строку кэша текущего процессора в недопустимое состояние.Когда процессор изменяет данные, он повторно считывает базу данных из системной памяти для обработки.на сервере кеш.
4. Модель памяти Java (JMM)
- Спецификация виртуальной машины Java пытается определить модель памяти Java дляСкрывает различия в доступе к памяти различного оборудования и операционных систем., чтобы Java-программы могли достигать согласованных эффектов доступа к памяти на различных платформах.
- Модель памяти Javaаналогияв модели памяти компьютера.
- Для повышения производительности выполнения модель памяти Java не ограничивает механизм выполнения в использовании определенных регистров или кэшей процессора для работы с основной памятью, а также не ограничивает компилятор в настройке оптимизации порядка кода. Итак, модель памяти JavaБудут проблемы с когерентностью кеша и проблемы с переупорядочением инструкций..
- Модель памяти Java предусматривает, что все переменные хранятся в основной памяти (аналогично физической памяти в модели компьютера), и каждый поток имеет свою собственную рабочую память (аналогично кэшу модели компьютера). здесьПеременнаявключая переменные экземпляра и статические переменные, ноне включает локальные переменные, потому что локальные переменные являются потокозависимыми.
- Рабочая память потока содержит копию основной памяти переменных, используемых этим потоком.Все операции над переменными потоком должны выполняться в рабочей памяти., вместо того, чтобы напрямую работать с основной памятью. И каждый поток не может получить доступ к рабочей памяти других потоков.
Например, предположив, что начальное значение i равно 0, выполните следующую инструкцию:
i = i+1;
Сначала поток выполнения t1 считывает i=0 из основной памяти в рабочую память. Затем в рабочей памяти назначьте i+1, рабочая память получит i=1 и, наконец, запишите результат обратно в основную память. Поэтому, если это один поток, выполнение оператора не представляет проблемы. Однако срок действия локальной рабочей памяти потока t2 не истек, поэтому данные, которые он считывает, являются грязными. Как показано на рисунке:
Модель памяти Java построена на том, как обрабатываются параллельные процессы.Атомарность, видимость и порядокЭти три характеристики установлены, давайте рассмотрим их вместе~
5. Три характеристики параллельного программирования (атомарность, видимость, упорядоченность)
атомарность
Атомарность означает, что операция непрерывна, либо выполнение завершено, либо не выполнено.Доступ, чтение и запись базовых типов данных, конечно, атомарны (за исключением неатомарного контракта long и double). Давайте рассмотрим несколько небольших примеров:
i =666; // 语句1
i = j; // 语句2
i = i+1; //语句 3
i++; // 语句4
- Операция оператора 1 заведомо атомарна, присваивая i значение 666, то есть, когда поток выполняет этот оператор, он напрямую записывает значение 666 в рабочую память.
- Операция оператора 2 также кажется атомарной, но на самом деле она включает в себя две операции: сначала прочитать значение j, а затем записать значение j в рабочую память, две операции по отдельности являются атомарными, но они не объединяются. , Атомарность удовлетворена.
- Оператор 3 считывает значение i, добавляет 1 и записывает его обратно в основную память Это не атомарная операция.
- Оператор 4 эквивалентен оператору 3 и также является неатомарной операцией.
видимость
-
Видимость означает, что когда поток изменяет значение общей переменной, другие потоки могут немедленно узнать об изменении.
-
Модель памяти Java обеспечивает видимость за счет синхронизации нового значения обратно в основную память после изменения переменной и обновления значения переменной из основной памяти до того, как переменная будет прочитана.
-
Изменчивая переменная гарантирует, что новое значение может быть немедленно синхронизировано обратно в основную память и обновлено из основной памяти непосредственно перед каждым использованием, поэтому мы говорим, что изменчивая гарантирует видимость переменных, которыми манипулируют несколько потоков.
-
Синхронизация и блокировка также могут обеспечить видимость, и поток сбрасывает значение общей переменной обратно в основную память перед снятием блокировки. final также может реализовать видимость.
упорядоченность
Виртуальная машина Java описывает упорядоченность Java-программ следующим образом: если наблюдается в этом потоке, все операции упорядочены, если один поток наблюдается в другом потоке, все операции не в порядке.
Вторая половина предложения означает, что в модели памяти JavaПозволяет компилятору и процессору изменять порядок инструкций, что повлияет на корректность многопоточного параллельного выполнения; первая половина предложения означает, чтоas-if-serialСемантика, то есть как бы ни переупорядочивались (компилятор и процессор для улучшения параллелизма), результат выполнения (однопоточной) программы не изменится.
Например, следующий программный код:
double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r * r; //C
Шаг C зависит от шагов A и B. Из-за перестановки инструкций последовательность выполнения программы может быть A->B->C или B->A->C, но C не может быть выполнена раньше A или B. , что нарушило бы семантику "как-будто-сериал".
Посмотрите на код. Предположим, что программа сначала выполняет метод чтения, а затем выполняет метод добавления.Результатом должна быть выходная сумма = 2?
bool flag = false;
int b = 0;
public void read() {
b = 1; //1
flag = true; //2
}
public void add() {
if (flag) { //3
int sum =b+b; //4
System.out.println("bb sum is"+sum);
}
}
Если это один поток, результат должен быть хорошим, если он многопоточный, поток t1 выполняет шаги 1 и 2.изменение порядка инструкцийШерстяная ткань? Итоговая сумма равна не 2, а 0, как показано на следующем рисунке:
Почему это?изменение порядка инструкцийЧтобы понять, перестановка инструкций относится к процессу выполнения программы,Для повышения производительности, Компилятор и ЦП могут переупорядочивать инструкции. Переупорядочивание процессора включает в себя параллельное переупорядочивание команд и переупорядочивание системы памяти.Тип переупорядочивания и процесс выполнения переупорядочивания следующие:
На самом деле ключевое слово volatile может быть добавлено к флагу, чтобы обеспечить упорядоченность. Конечно, порядок также может быть гарантирован синхронизацией и блокировкой. Synchronized и Lock гарантируют, что только один поток выполняет код синхронизации в определенное время, что эквивалентно разрешению потокам выполнять программный код последовательно, что, естественно, обеспечивает упорядоченность.
На самом деле порядок модели памяти Java не гарантируется только volatile, synchronized и Lock. Это связано с тем, что в языке Java существует принцип «происходит до»:
- правила порядка программы: В потоке, в порядке потока управления, операция, написанная впереди, выполняется до операции, написанной сзади.
- Правила блокировки монитора: сначала выполняется операция разблокировки, а затем выполняется такая же операция блокировки.
- правила изменяемой переменной: запись в переменную происходит перед последующим чтением переменной
- правило начала потока: метод start() объекта Thread выполняется первым для каждого действия этого потока.
- правила завершения потока: Все операции в потоке выполняются первыми при обнаружении завершения потока.Мы можем определить, что поток завершил выполнение, через конец метода Thread.join() и возвращаемое значение Thread.isAlive().
- Правила прерывания потока: вызов метода прерывания потока () происходит первым, когда код прерванного потока обнаруживает возникновение события прерывания.
- Правила завершения объекта: инициализация объекта происходит сначала в начале его метода finalize().
- переходность: Если операция А выполняется до операции В, а операция В выполняется до операции С, можно сделать вывод, что операция А выполняется до операции С.
Согласно восьми правилам «случается раньше», давайте вернемся к примеру и проанализируем его вместе. Добавьте к флагу ключевое слово volatile, посмотрите, как оно гарантирует упорядоченность,
volatile bool flag = false;
int b = 0;
public void read() {
b = 1; //1
flag = true; //2
}
public void add() {
if (flag) { //3
int sum =b+b; //4
System.out.println("bb sum is"+sum);
}
}
- Во-первых, добавление к флагу ключевого слова volatile запрещает переупорядочивание команд, то есть 1 происходит раньше 2.
- в соответствии справила изменяемой переменной, 2 бывает-до 3
- Зависит отправила порядка программы, в результате чего 3 происходит до 4
- Наконец,переходность, получить 1 происходит раньше 4, поэтому правильная выходная сумма = 2~
6. Основной принцип изменчивости
Из приведенного выше обсуждения мы знаем, что семантика volatile заключается в том, чтобы обеспечить видимость переменных для всех потоков и запретить оптимизацию перестановки команд. Итак, как его нижний слой обеспечивает видимость и запрещает изменение порядка инструкций?
Как volatile гарантирует видимость?
Вот, взгляните сначала на несколько фотографий, ха-ха~
Предполагая, что начальное значение переменной флага равно false, и теперь есть два потока t1 и t2 для доступа к нему, это можно упростить до следующей схемы:
Если поток t1 выполняет следующий оператор кода, а флаг не изменен volatile; t1 только что изменил значение флага и не успел обновить основную память, а t2 запускается, чтобы прочитать его снова, и данные flag легко противоречит следующим образом:
flag=true;
Если переменная флага изменяется с помощью volatile, она отличается.Если поток t1 изменяет значение флага, volatile может гарантировать, что измененная переменная флага может бытьНемедленная синхронизация обратно в основную память. Как показано на рисунке:
Внимательные друзья обнаружат, что поток T2 не является старым значением флага, это не проблема? На самом деле у Volatile есть гарантия, т.е.Сбрасывать последнее значение из основной памяти непосредственно перед каждым использованием, после изменения потока t1 срок действия переменной копии потока t2 истечет, как показано на рисунке:
Очевидно, это не нижний слой, на самом деле volatile гарантирует видимость и запрещает перестановку инструкций.барьер памятиАктуально, давайте скомпилируем код, связанный с volatile, и посмотрим~
Одноэлементный режим DCL (изменчивый) и сравнение компиляции
Одноэлементный режим DCL (Double Check Lock, двойная проверка блокировки) используется чаще, он требует изменчивой модификации, поэтому просто скомпилируйте этот код
public class Singleton {
private volatile static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
После компиляции этого кода посмотрите на ассемблерный код, сгенерированный экземпляром с ключевым словом volatile и без ключевого слова volatile, и обнаружите, что при изменении ключевого слова volatile появится дополнительная блокировка addl $0x0,(%esp), то есть , дополнительная инструкция префикса блокировки
0x01a3de0f: mov $0x3375cdb0,%esi ;...beb0cd75 33
; {oop('Singleton')}
0x01a3de14: mov %eax,0x150(%esi) ;...89865001 0000
0x01a3de1a: shr $0x9,%esi ;...c1ee09
0x01a3de1d: movb $0x0,0x1104800(%esi) ;...c6860048 100100
0x01a3de24: lock addl $0x0,(%esp) ;...f0830424 00
;*putstatic instance
; - Singleton::getInstance@24
Инструкция блокировки эквивалентнабарьер памяти, что гарантирует следующее:
- 1. При переупорядочивании следующие инструкции нельзя переупорядочить до положения перед барьером памяти.
- 2. Записать кэш этого процессора в память
- 3. Если это действие записи, оно сделает недействительным соответствующий кеш в других процессорах.
Очевидно, что пункты 2 и 3 не являются воплощением volatile, гарантирующего видимость, а пункт 1 — воплощением запрета перестановки инструкций.
барьер памяти
Существует четыре основных категории барьеров памяти: (Загрузка представляет инструкции чтения, Сохранение представляет инструкции записи).
тип барьера памяти | абстрактная сцена | описывать |
---|---|---|
НагрузкаГрузовой барьер | Load1; LoadLoad; Load2 | Перед обращением к данным, которые должны быть прочитаны Load2, убедитесь, что данные, которые должны быть прочитаны Load1, были прочитаны. |
МагазинМагазин Барьер | Store1; StoreStore; Store2 | Перед выполнением записи Store2 убедитесь, что операции записи Store1 видны другим процессорам. |
Барьер LoadStore | Load1; LoadStore; Store2 | Прежде чем Store2 будет записан, убедитесь, что данные, которые должны быть прочитаны Load1, были прочитаны. |
Барьер StoreLoad | Store1; StoreLoad; Load2 | Запись в Store1 гарантированно будет видна всем процессорам до чтения Load2. |
Чтобы достичь семантики памяти volatile, модель памяти Java использует следующую консервативную стратегию.
- Вставьте барьер StoreStore перед каждой операцией энергозависимой записи.
- Вставляйте барьер StoreLoad после каждой операции энергозависимой записи.
- Вставьте барьер LoadLoad после каждого чтения volatile.
- Вставляйте барьер LoadStore после каждого чтения энергозависимых данных.
Некоторые друзья могут все еще немного смущаться по этому поводу, барьер памяти слишком абстрактен. Посмотрим на код (за флажком тоже стоит барьер памяти LoadLoad, картинка неправильная):
Барьер памяти гарантирует, что предыдущие инструкции будут выполняться первыми, поэтому это гарантирует, что изменение порядка инструкций запрещено.В то же время барьер памяти гарантирует, что кеш записывается в память, а другие кеши процессора становятся недействительными, что также обеспечивает видимость, ха-ха ~
7. Типичные сценарии волатильности
Вообще говоря, использование volatile должно соответствовать следующим двум условиям:
- 1) Операции записи в переменные не зависят от текущего значения
- 2) Переменная не входит в инвариант с другими переменными
На самом деле сценарии нестабильности, как правило,флаг состояния,а такжеОдноэлементный шаблон DCL.
7.1 Флаги состояния
Углубленное понимание виртуальной машины Java, примеры в книге:
Map configOptions;
char[] configText;
// 此变量必须定义为 volatile
volatile boolean initialized = false;
// 假设以下代码在线程 A 中运行
// 模拟读取配置信息, 当读取完成后将 initialized 设置为 true 以告知其他线程配置可用
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
// 假设以下代码在线程 B 中运行
// 等待 initialized 为 true, 代表线程 A 已经把配置信息初始化完成
while(!initialized) {
sleep();
}
// 使用线程 A 中初始化好的配置信息
doSomethingWithConfig();
7.2 Одноэлементный шаблон DCL
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
8. классические вопросы на собеседовании, связанные с изменчивостью
- Расскажите о характеристиках летучих
- семантика энергозависимой памяти
- Расскажите о трех основных характеристиках параллельного программирования.
- Что такое видимость памяти и что такое переупорядочение инструкций?
- Как volatile решает проблему видимости в параллелизме Java
- Как volatile предотвращает перестановку инструкций
- Может ли volatile решить атомарность? Зачем?
- Основной механизм реализации volatile
- В чем разница между volatile и синхронизированным?
8.1 Расскажите о характеристиках volatile
8.2 Семантика памяти volatile
- При записи изменчивой переменной JMM сбрасывает значение общей переменной из соответствующей локальной памяти потока в основную память.
- При чтении энергозависимой переменной JMM аннулирует соответствующую локальную память потока. Затем поток будет читать общую переменную из основной памяти.
8.3 Расскажите о трех основных характеристиках параллельного программирования
- атомарность
- видимость
- упорядоченность
8.4 Что такое видимость памяти и что такое переупорядочивание инструкций?
- Видимость означает, что когда поток изменяет значение общей переменной, другие потоки могут немедленно узнать об изменении.
- Перестановка инструкций относится к переупорядочению существующего порядка инструкций, когда JVM компилирует код Java или когда ЦП выполняет байт-код JVM.
8.5 Как volatile решает проблему видимости в параллелизме Java
Нижний уровень реализуется через барьеры памяти.Volatile может гарантировать, что измененная переменная может быть немедленно синхронизирована обратно в основную память, а последнее значение обновляется из основной памяти непосредственно перед каждым использованием.
8.6 Как volatile предотвращает перестановку команд
Это также барьер памяти.Расскажите интервьюеру о консервативной стратегии памяти Java:
- Вставьте барьер StoreStore перед каждой операцией энергозависимой записи.
- Вставляйте барьер StoreLoad после каждой операции энергозависимой записи.
- Вставьте барьер LoadLoad после каждого чтения volatile.
- Вставляйте барьер LoadStore после каждого чтения энергозависимых данных.
Давайте еще раз поговорим о семантике volatile: при переупорядочивании инструкции за барьером памяти не могут быть переупорядочены на позицию перед барьером памяти.
8.7 Может ли volatile решить проблему атомарности? Зачем?
Нет, вы можете прямо привести пример i++, атомарность требует гарантии синхронизации или блокировки
public class Test {
public volatile int race = 0;
public void increase() {
race++;
}
public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<100;j++)
test.increase();
};
}.start();
}
//等待所有累加线程结束
while(Thread.activeCount()>1)
Thread.yield();
System.out.println(test.race);
}
}
8.8 Базовый механизм реализации volatile
Вы можете увидеть шестой раздел этой статьи, основной принцип volatile, в основном вы должны рассказать интервьюеру, как volatile обеспечивает видимость и запрещает перестановку инструкций, и вам нужно поговорить о барьерах памяти~
8.9 В чем разница между энергозависимой и синхронизированной?
- Volatile изменяет переменные, а synchronized обычно изменяет блоки или методы кода.
- Volatile гарантирует видимость и запрещает переупорядочивание инструкций, но не гарантирует атомарность; synchronized может гарантировать атомарность
- Volatile не вызовет блокировку потока, синхронизированный может вызвать блокировку потока, так что позже будет так много историй об оптимизации блокировок ~
- Ха-ха, есть что добавить~
Рекомендовать ранее написанную статью:Синхронный анализ - если вы готовы чистить мое сердце слой за слоем
публика
Ссылка и спасибо
- >
- Параллельное программирование на Java: анализ ключевых слов volatile
- Любимое изменчивое ключевое слово интервьюера
- Интервьюер не ожидал Летучего, я мог говорить с ним полчаса
- Если кто-то спросит вас, что такое модель памяти Java, пришлите ему эту статью.
- [Параллельное программирование] MESI--CPU Cache Coherence Protocol
- Комикс: Влияние volatile на перестановку инструкций
- Подробное объяснение трех характеристик volatile