атомарность
Атомарность относится к свойству, при котором одна или несколько операций не прерываются во время выполнения ЦП.
переключение потоковпроблема атомарности
Параллельные программы Java основаны на многопоточности.Чтобы в полной мере использовать ресурсы ЦП, операционная система делит ЦП на несколько временных интервалов.В многопоточной среде потоки планируются операционной системой для переключения задач.
Чтобы интуитивно понять, что такое атомарность, давайте посмотрим, какие из следующих операций являются атомарными операциями.
int count = 0; //1
count++; //2
int a = count; //3
В операторах, показанных выше, за исключением оператора 1, который является атомарной операцией, два других оператора не являются атомарными операциями Давайте проанализируем оператор 2.
Фактически, когда оператор 2 выполняется, он содержит три командных операции.
- Инструкция 1: Во-первых, переменная count должна быть загружена из памяти в регистр ЦП.
- Инструкция 2: После этого выполнить операцию +1 в реестре;
- Инструкция 3: Наконец, запишите результат в память
Для приведенных выше трех инструкций, если поток A выполняет переключение потока после выполнения инструкции 1, а поток A и поток B выполняются в последовательности, показанной на следующем рисунке, то мы обнаружим, что оба потока выполнили операцию count+=1. , но Результат не тот 2, которого мы ожидали, а 1.
Операционная система выполняет переключение задач, которое может произойти после выполнения любой инструкции ЦП.
упорядоченность
Упорядоченный означает, что программа выполняется в том порядке, в котором выполняется код.
**Оптимизация компиляции **Проблема упорядочивания, вызванная
Для оптимизации производительности компилятор и процессор переупорядочивают инструкции, иногда изменяя порядок операторов в программе, например, программа:
a = 5; //1
b = 20; //2
c = a + b; //3
После оптимизации компилятора может стать
b = 20; //1
a = 5; //2
c = a + b; //3
В этом примере компилятор корректирует порядок операторов, не влияя на конечный результат программы.
synchronized(с порядком, атомарностью и видимостью) означает, что блокировка может быть получена только одним потоком одновременно, и когда блокировка занята, другие потоки могут только ждать.
Существует способ блокировки с двойной проверкой в реализации одноэлементного паттерна (Double-checked Locking)
public class Singleton {
static Singleton instance;
static Singleton getInstance(){
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
давайте сначала посмотримinstance = new Singleton()
операции, не оптимизированные компилятором
-
Инструкция 1: выделить блок памяти M;
-
Инструкция 2: Инициализируйте объект Singleton в памяти M;
-
Инструкция 3: Затем присвойте адрес M переменной экземпляра.
Инструкции по эксплуатации, оптимизированные для компилятора
-
Инструкция 1: выделить блок памяти M;
-
Инструкция 2: Назначьте адрес M переменной экземпляра;
-
Инструкция 3: Затем инициализируйте объект Singleton в памяти M.
Теперь есть два потока A и B, мы предполагаем, что поток A выполняется первымgetInstance()
метод при выполнении инструкции, оптимизированной для компилятора2
В это время (инициализация объекта в это время не завершена) в это время происходит переключение потока, затем входит поток B и как раз выполняет первое суждениеinstance==null
найдуinstance
не равноnull
, поэтому вернитесь напрямуюinstance
, и экземпляр в это время не был инициализирован.
В настоящее время более распространенной практикой является использование статических внутренних классов для достижения
public class SingletonDemo {
private SingletonDemo() {
}
private static class SingletonDemoHandler{
private static SingletonDemo instance = new SingletonDemo();
}
public static SingletonDemo getInstance() {
return SingletonDemoHandler.instance;
}
}
видимость
Видимость означает, что когда поток изменяет общую переменную, другие потоки могут немедленно узнать об изменении.
тайникПроблемы с видимостью, вызванные
Сначала давайте посмотрим на модель памяти Java (JMM).
- Все определяемые нами переменные хранятся в
主内存
середина - У каждой нити своя
独立的工作内存
, который содержит копию переменной, используемой потоком (копию переменной в основной памяти) - Все операции потоков над общими переменными должны выполняться в их собственной рабочей памяти и не могут быть напрямую прочитаны и записаны из основной памяти (не могут перескакивать)
- К переменным в рабочей памяти других потоков нельзя обращаться напрямую между разными потоками, а передачу значений переменных между потоками нужно выполнять через основную память. (Братья и сестры не могут получить доступ друг к другу)
Принцип реализации видимости разделяемой переменной:
Если поток 1 модифицирует общую переменную, он должен вовремя увидеть поток 2, он должен выполнить следующие шаги:
- Сбросить обновленное значение переменной из рабочей памяти 1 в основную память
- Обновить значение переменной в основной памяти до рабочей памяти 2
можно использоватьsynchronized,volatile,finalчтобы обеспечить видимость
Добро пожаловать в публичный аккаунт