1. Модель памяти Java
Отношения чтения-записи потоков, рабочей памяти и переменных основной памяти в основном описаны здесь:
- В основной памяти хранятся переменные, необходимые потоку для работы, но поток напрямую не работает с основной памятью.
- Каждый поток считывает переменную основной памяти, предварительно копируя ее в рабочую память, причем рабочая память разных потоков не мешает друг другу.
- После того, как поток модифицирует рабочую память, он выполняет обратную запись в основную память.
- Каждое чтение и запись из основной памяти требует 8 атомарных операций.
2. изменчивая видимость
1. Особенности летучих
(1) Операции чтения и загрузки должны выполняться перед использованием.
(2) Операции сохранения и записи должны выполняться после операций назначения.
Оперативная непрерывность чтения, загрузки и использования, а также оперативная непрерывность назначения, сохранения и записи гарантируются характеристиками, так что последнее значение основной памяти должно быть обновлено до того, как рабочая память будет прочитана; рабочая память должна быть синхронизируется с основной памятью после записи. .Непрерывность чтения и непрерывность записи, похоже, что поток напрямую манипулирует основной памятью..
Расширение:Операции блокировки и разблокировки не открыты для пользователя напрямую, но неявно доступны для monitorenter и monitorexit, указанных в ключевом слове Synchronize. Что касается монитора блокировки монитора Synchronize, то после компиляции javac инструкции monitorenter и monitorexit будут добавлены до и после функционального метода.Для получения подробной информации вы можете проверить принцип синхронизации.
2. Видимость проверки кода
public class VolatileVisibility {
public static class TestData {
volatile int num = 0;
public void updateNum(){
num = 1;
}
}
public static void main(String[] args) {
final TestData testData = new TestData();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("ChildThread num-->"+testData.num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
testData.updateNum();
System.out.println("ChildThread update num-->"+testData.num);
}
}).start();
while (testData.num == 0){
}
System.out.println("MainThread num-->"+testData.num);
}
}
(1) TestData
номер не добавляетсяvolatile
ключевые слова,System.out.println("MainThread num-->"+testData.num);
Это предложение так и не было реализовано. Указывает, что условие testData.num == 0 в то время как всегда равно 0, и модификация num подпотоком не влияет на основной поток.
(2) TestData
число в добавленииvolatile
ключевые слова,System.out.println("MainThread num-->"+testData.num);
будет выполняться, и результат будет следующим.
ChildThread num-->0
ChildThread update num-->1
MainThread num-->1
3. изменчивая неатомарность
1. Две операции use и assign в целом не являются непрерывной атомарной операцией.
Сам Volatile не поддерживает атомарность обработки операций с данными, подчеркивая, что чтение и запись влияют на основную память во времени.
2. Неатомарные операции
volatile изменяет num, num++; num = num+1; это неатомарная операция.
(1) Основная память считывает значение num;
(2) Выполнить операцию num++;
(3) Запишите значение num в оперативную память.
В многопоточной среде использование и назначение появляются несколько раз.Если два потока читают num в основную память и одновременно выполняют num++, результатом обоих потоков будет 3. Генерируются грязные данные, а затем записываются в основную память. это 3. Не гарантируется последовательное выполнение основных операций num++. Чтобы гарантировать порядок потоков, в котором выполняются операции, можно выбрать «Синхронизировать».
3. Проверка кода неатомарна
public class ValatileAtomic {
public static class TestData {
volatile int num = 0;
//synchronized
public void updateNum(){
num++;
}
}
public static void main(String[] args) {
final TestData testData = new TestData();
for(int i = 1; i <= 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 1; j <= 1000; j++) {
testData.updateNum();
}
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println("最终结果:" + testData.num);
}
}
По нашим пожеланиям, 10 потоков, каждый поток накапливает 1000 потоков, всего 10 * 1000 = 10000. ноvolatile int num = 0;
Используется ли volatile или нет, отражает неатомарность, а результат операции меньше 10000:
最终结果:9701
Чтобы добиться синхронизации, добавьте ключевое слово synchronize перед методом updateNum():
最终结果:10000
4. изменчивая упорядоченность
1. volatile запрещает перестановку инструкций
(1) Переупорядочивание инструкций. Для повышения производительности компиляторы и процессоры обычно переупорядочивают инструкции.
Три положения перестановки на рисунке можно менять местами и переставлять в соответствии с потребностями оптимизации системы. Принцип, которому следует следовать, заключается в том, что результат выполнения после перераспределения одного потока должен быть таким же, как и результат последовательного выполнения.(2) Инструкции барьера памяти: volatile вставляет барьеры памяти между инструкциями, чтобы обеспечить выполнение в определенном порядке и видимость определенных переменных.
Volatile уведомляет процессор и компилятор о том, что они не должны выполнять оптимизацию перестановки инструкций через барьеры памяти для поддержания упорядоченности.
2. синхронизировать последовательное управление
(1) синхронизация не запрещает перестановку инструкций.
(2) Только одному потоку переменной разрешено одновременно выполнять операцию блокировки для получения блокировки объекта, а взаимное исключение и исключительность могут обеспечить последовательное выполнение двух синхронизированных блоков.
5. Сфера применения энергозависимой безопасности потоков
Из-за неатомарной природы volatile его потокобезопасность условна:
(1) Результат операции не является зависимым, а предшествующим, или может быть гарантировано свободное изменение значения переменной одним потоком.
(2) Переменные не должны участвовать в постоянных ограничениях с другими переменными состояния. Эти два условия описаны в разделе «Углубленное понимание виртуальной машины Java».
6. Использование volatile с синхронизацией
1. Одноэлементный код DCL
public 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;
}
}
2. Зачем использовать volatile для модификации
В соответствии с описанным выше методом написания операция new Singleton(); была синхронизирована, что гарантирует, что несколько потоков могут выполнять этот код инстанцирования только последовательно. На самом деле синхронизация гарантирует, что код создания экземпляра выполнения потока является последовательным, но у Synchronize нет функции запрета переупорядочивания инструкций.
иinstance = new Singleton();
В основном делайте 3 вещи:
(1) Виртуальная машина Java выделяет часть памяти x для объекта.
(2) Инициализировать объект в памяти x.
(3) Назначьте адрес памяти x переменной экземпляра.
Если компилятор перестраивается на:
(1) Виртуальная машина Java выделяет часть памяти x для объекта.
(2) Назначьте адрес памяти x переменной экземпляра.
(3) Инициализировать объект в памяти x.
Первый случай, без изменчивой модификации: в это время есть два потока, выполняющих метод getInstance(), добавляющий поток A для ввода второй точки в комментарии кода и выполняющий (2) измененную инструкцию в момент в то же время поток B Просто если решение в ① в комментарии к коду. В это время у экземпляра есть поток A, назначающий экземпляру адрес памяти x адрес, тогда экземпляр не пуст, но не был инициализирован, а поток B возвращает экземпляр, который не был инициализирован, и произойдет ошибка нулевого указателя. когда он, наконец, используется.
Во втором случае существует изменчивая модификация: поскольку экземпляр является изменчивым, что запрещает переупорядочивание инструкций, он только сначала установит объект, а затем назначит его экземпляру для выполнения по порядку, чтобы можно было гарантировать нормальный экземпляр объекта. быть возвращенным.
7. Резюме
1.volatile имеет видимость и порядок и не может гарантировать атомарность.
2. Volatile является потокобезопасным при определенных обстоятельствах, например, если он не выполняет неатомарные операции сам по себе.
3.synchronize обеспечивает последовательное выполнение блоков кода путем получения объектных блокировок без возможности запретить перестановку инструкций.
4. Одноэлементные операции DCL требуют энергозависимости и синхронизации для обеспечения безопасности потоков.