Атомарность, упорядоченность и прозрачность параллелизма в Java

Java
атомарность

Атомарность относится к свойству, при котором одна или несколько операций не прерываются во время выполнения ЦП.

переключение потоковпроблема атомарности

Параллельные программы Java основаны на многопоточности.Чтобы в полной мере использовать ресурсы ЦП, операционная система делит ЦП на несколько временных интервалов.В многопоточной среде потоки планируются операционной системой для переключения задач.

image-20190304155302961

Чтобы интуитивно понять, что такое атомарность, давайте посмотрим, какие из следующих операций являются атомарными операциями.

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.

image-20190304163607329

Операционная система выполняет переключение задач, которое может произойти после выполнения любой инструкции ЦП.

упорядоченность

Упорядоченный означает, что программа выполняется в том порядке, в котором выполняется код.

**Оптимизация компиляции **Проблема упорядочивания, вызванная

Для оптимизации производительности компилятор и процессор переупорядочивают инструкции, иногда изменяя порядок операторов в программе, например, программа:

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, и экземпляр в это время не был инициализирован.

image-20190304173844373

В настоящее время более распространенной практикой является использование статических внутренних классов для достижения

public class SingletonDemo {
    private SingletonDemo() {
    }
    private static class SingletonDemoHandler{
        private static SingletonDemo instance = new SingletonDemo();
    }
    public static SingletonDemo getInstance() {
        return SingletonDemoHandler.instance;
    }
}
видимость

Видимость означает, что когда поток изменяет общую переменную, другие потоки могут немедленно узнать об изменении.

тайникПроблемы с видимостью, вызванные

Сначала давайте посмотрим на модель памяти Java (JMM).

image-20190304183341997

  • Все определяемые нами переменные хранятся в主内存середина
  • У каждой нити своя独立的工作内存, который содержит копию переменной, используемой потоком (копию переменной в основной памяти)
  • Все операции потоков над общими переменными должны выполняться в их собственной рабочей памяти и не могут быть напрямую прочитаны и записаны из основной памяти (не могут перескакивать)
  • К переменным в рабочей памяти других потоков нельзя обращаться напрямую между разными потоками, а передачу значений переменных между потоками нужно выполнять через основную память. (Братья и сестры не могут получить доступ друг к другу)

Принцип реализации видимости разделяемой переменной:

Если поток 1 модифицирует общую переменную, он должен вовремя увидеть поток 2, он должен выполнить следующие шаги:

  1. Сбросить обновленное значение переменной из рабочей памяти 1 в основную память
  2. Обновить значение переменной в основной памяти до рабочей памяти 2

можно использоватьsynchronized,volatile,finalчтобы обеспечить видимость

Добро пожаловать в публичный аккаунт

image-20190304183341997