Углубленное понимание модели памяти Java (четыре) - летучий

Характеристики летучих

Когда мы объявляем общие переменные летучими, чтение / запись будет очень особенным для этой переменной. Хороший способ понять летучие характеристики: один чтение летучих переменные / записи, используя один и тот же монитор, как замок для одной операции чтения / записи сделана в синхронизации. Пусть иллюстрируются конкретными примерами, см следующий код:

class VolatileFeaturesExample {
    volatile long vl = 0L;  // 使用 volatile 声明 64 位的 long 型变量 

    public void set(long l) {
        vl = l;   // 单个 volatile 变量的写 
    }

    public void getAndIncrement () {
        vl++;    // 复合(多个)volatile 变量的读 / 写 
    }


    public long get() {
        return vl;   // 单个 volatile 变量的读 
    }
}

Предположим, что существует несколько потоков называют три метода выше программы, эта программа является семантический эквивалентной и следующими процедурами: 

class VolatileFeaturesExample {
    long vl = 0L;               // 64 位的 long 型普通变量 

    public synchronized void set(long l) {     // 对单个的普通 变量的写用同一个监视器同步 
        vl = l;
    }

    public void getAndIncrement () { // 普通方法调用 
        long temp = get();           // 调用已同步的读方法 
        temp += 1L;                  // 普通写操作 
        set(temp);                   // 调用已同步的写方法 
    }
    public synchronized long get() { 
    // 对单个的普通变量的读用同一个监视器同步 
        return vl;
    }
}

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

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

Монитор блокировка семантика определяет выполнение критической секции кода атомного. Это означает, что даже 64-разрядные и переменный двойной тип, до тех пор, как он является летучим переменным, переменным будут читать атомарными. Если множество операций или аналогичных летучего ++ летучего соединения таких операций, которые не имеет атомные целом.

Короче говоря, сам по себе летучим переменному имеют следующие характеристики:

  • Видимость. Чтение летучего переменный, всегда в состоянии видеть (любые темы) Последние записи этого летучие переменные.
  • Атомарность: чтение любой одной переменной летучий / записи атомной, но похожа на такие составные операции не является летучей ++ атомной.

летучая запись - происходит до установления взаимосвязи между чтением

Упомянутые выше Летучие переменные собственные характеристики, программист, влияние энергозависимой памяти видимость потока является более важным, чем летучие собственные характеристики, но и нуждается в нашем внимании.

Запуск JSR-133, энергозависимая переменная запись - читать связи между потоками могут достичь.

Из памяти семантической точки зрения, с замком монитора летучими имеют тот же эффект: выделение летучих записи и монитор имеет одинаковую смысловую память, читайте монитор эквайринга энергонезависимой памяти имеет ту же семантику.

См пример кода ниже летучих переменных:

class VolatileExample {
    int a = 0;
    volatile boolean flag = false;

    public void writer() {
        a = 1;                   //1
        flag = true;               //2
    }

    public void reader() {
        if (flag) {                //3
            int i =  a;           //4
            ……
        }
    }
}

После того, как поток исполнения А метод предполагаемый автор (), поток B выполняется метод чтения (). Согласно происходит перед правилами, процесс, который происходит до установления отношений можно разделить на две категории:

  1. Правила последовательности программы, 1 происходит до 2; 3 происходит до 4.
  2. Летучий правило, 2 происходит до 3.
  3. Передача происходит до того, как правила, 1 происходит до 4.

Узорное проявление до отношений происходит следующим образом:

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

Нить здесь после написания летучего переменного, B нить читать же летучие переменную. Поток перед записью летучего переменных все видимые общие переменным, нить B после прочтения же летучих переменного, сразу же станешь виден нитью B.

летучая записи - читать семантическую память

семантика записи энергозависимой памяти является следующей:

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

В приведенном выше примере программы VolatileExample Например, предположим, что сначала выполняет поток Писатель метод (), а затем поток В выполняется метод чтения (), начальный две нити локальной памяти и флаг находятся в исходном состоянии. Фигура выполняются после нити летучей письменной формы, состояние общего переменной схемы:

Как было показано выше, после записи переменной флаг нити А, значение локальной памяти потока обновляется два общие переменным сбрасываются в основную память. В это время, значение локальной памяти и основной памяти общей переменной являются последовательными.

Летучие семантика памяти следующим образом:

  • При чтении летучий переменной, JMM будет поток, соответствующий локальной памяти сбрасывается. Тогда основной поток чтения общих переменных из памяти.

Ниже приводится поток В читает тот же летучий переменную, схематический вид состояния общих переменных:

Как было показано выше, после прочтения переменной флаг, локальная память B был сброшен. В это время, поток В должен быть считаны из основной памяти совместно переменных. Чтение операции нить B будет вызывать значение В и локальное запоминающее устройство совместно переменных в основной памяти также становится такой же.

Если мы летучие записи и летучий чтения этих два шага вместе, чтобы увидеть, если после прочтения нити B читают летучую переменное, запись нитяные до написания этого летучих переменного все видимых значения общего переменных сразу же станут читать темы В видимой.

Далее, летучая и нестабильная Семантика памяти для чтения и записи, чтобы быть резюме:

  • Поток написать летучую переменный, является по существу поток А выдается (где изменение его общего переменный) к следующему сообщению будет прочитана этим изменчивым переменной нить.
  • Тема B читает летучую переменную, по существу, нить B получил (прежде чем писать эту летучие переменные изменения, внесенные в общем переменных) сообщениях, отправленных на волоске раньше.
  • Поток представляет собой летучие переменные записи, затем поток Б считывает летучие переменный, процесс, по существу, поток А посылает сообщение к нити B через основную память.

семантическая реализация энергонезависимой памяти

Теперь посмотрим, давайте, как семантического ЯВМ памяти для достижения летучий записи / чтения.

Мы уже упоминали ранее скомпилированы в переназначения и сортировка обескуражены процессор переназначение. Для достижения летучей семантики памяти, JMM ограничить типы переназначения обоего типов, соответственно. Ниже приведен компилятор разработан для таблицы правил летучего переупорядочения JMM:

Может переназначение Вторая операция
Первая операция Обычное чтение / запись летучее чтение летучий записи
Обычное чтение / запись     НЕТ
летучее чтение НЕТ НЕТ НЕТ
летучий записи   НЕТ НЕТ

Например, третья строка из последних средств клеток: в программном порядке, когда первая операция чтение или запись простой переменная, если вторая операция записи, как летучие, компилятор не может это переупор две операции.

Из таблицы видно:

  • Когда вторая операция представляет собой летучее время записи, независимо от того, что не является первой операцией, которая не может быть заказана. Это правило гарантирует, что летучая операция записи, прежде чем не будет скомпилирован думает крайне неустойчивой после заказа писать.
  • Когда первая операция является летучим чтения, когда, независимо от того, что это вторая операция, она не может быть заказана. Это правило гарантирует, что летучая операция чтения не будет компилироваться после заказа рекомендуется прочитать перед неустойчивым.
  • Когда первая операция записи является летучей, летучей второй операция для чтения, без изменения порядка.

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

  • Каждый предшествующей операции записи, чтобы вставить летучий StoreStore барьер.
  • Запись операции обратно вставляется в каждой из летучего StoreLoad барьера.
  • Вставка летучего LoadLoad барьера после каждой операции чтения.
  • Вставка летучего LoadStore барьера после каждой операции чтения.

Вставьте барьер памяти выше стратегий очень консервативен, но он может гарантировать, что любой процессор платформа, любая программа может получить правильную летучую семантику памяти.

Следующий является консервативной стратегией, энергозависимая последовательность схематичной команды записи генерируется после вставки барьеров памяти:

На рисунке выше StoreStore барьера может быть гарантирован до летучей передней записи всей обычной операции записи должен быть видны в любом из процессора. Это потому, что StoreStore барьер будет защищать все выше общей записи продувают в основную память до летучей записи.

Более интересным здесь является летучим запись StoreLoad барьер позади. Роль барьера, чтобы избежать летучий обратной записи и может иметь энергонезависимую переназначения чтения / записи. Поскольку компилятор часто не может точно судить позже писал в летучем, необходимо вставить StoreLoad барьер (например, летучий метод записи сразу после возвращения). Для того, чтобы обеспечить достижение энергонезависимой памяти семантический правильно, JMM здесь принял консервативную стратегию: В каждой из летучей обратной записи или вставить StoreLoad барьер перед каждым летучим чтением. С точки зрения общих соображений эффективности, JMM выбран за каждый вставлен в летучую записи StoreLoad барьер. Так как летучие записи - общий шаблон использования считываются семантика памяти: а поток записи для записи летучих переменных, несколько потоков для чтения читателя того же переменного летучих. Когда число потоков читать гораздо больше, чем пишет нить, выберите Вставить StoreLoad барьер после летучей записи принесет значительное улучшение эффективности. Отсюда можно увидеть особенность реализованной на ЯВМ: Сначала убедитесь, правильность, а затем перейти к достижению эффективности.

Ниже консервативная стратегия, летучие барьеры памяти команд чтения, генерируемых после вставки диаграммы последовательности:

Рисунок выше LoadLoad барьера, чтобы отключить процессор, чтобы прочитать выше и ниже нормального летучего чтения переупорядочения. LoadStore барьер, чтобы отключить процессор, чтобы прочитать выше и ниже нормального летучего переупорядочения записи.

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

class VolatileBarrierExample {
    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    void readAndWrite() {
        int i = v1;           // 第一个 volatile 读 
        int j = v2;           // 第二个 volatile 读 
        a = i + j;            // 普通写 
        v1 = i + 1;          // 第一个 volatile 写 
        v2 = j * 2;          // 第二个 volatile 写 
    }

    …                    // 其他方法 
}

Для метода readAndWrite (), компилятор генерирует байт-код при оптимизации может быть сделано следующим образом:

Обратите внимание, что последний из StoreLoad барьера не может быть опущен. Потому что после того, как второй летучие методы записи вернуться немедленно. В это время, компилятор не может точно определить, будет ли летучим позже чтение или запись, по соображениям безопасности, компилятор часто вставить здесь StoreLoad барьер.

Выше оптимизация была для любого процессора платформы, из-за различные процессоры имеют разную «плотность» в модели памяти процессора, барьер памяти вставка может продолжать оптимизируют памяти процессора в зависимости от конкретной модели. В x86 процессоров, например, изображение выше для последнего барьера, за исключением StoreLoad другого барьера будет опущено.

летучее чтение и запись перед консервативной стратегией, на платформе x86 процессоров могут быть оптимизированы для:

Упомянутый ранее, x86 процессоры будут только записи - чтения этого переназначения. X86 не читает - читать, читать - писать и писать - операции записи этого переназначения, поэтому процессоры x86 опустим эти три операции, соответствующие типу барьера памяти. В x86, JMM только летучая обратная запись вставки StoreLoad барьера может быть реализована летучее записи правильно - семантика памяти для чтения. Это означает, что процессоры x86, летучие летучие накладные расходы для чтения записи, чем стоимость будут намного больше (потому что барьер над головой выполнения StoreLoad будет относительно большой).

Почему JSR-133 для улучшения летучей семантики памяти

В старой JSR-133 модели памяти Java раньше, хотя не допускается изменение порядка между летучими переменными, но старая модель памяти Java позволяет переназначения между летучими и регулярной переменной. В старой модели памяти, VolatileExample примерная программа может быть заказана в следующей последовательности выполняется:

В старой модели памяти, между 1 и 2, когда нет зависимостей данных, не могут быть переупорядоченными (аналогично 3 и 4) между 1 и 2. Результат: Чтение нить В выполняет 4, не может быть в состоянии увидеть модификацию пишущего нити А в реализации общих переменных 1:00.

Таким образом, в старой модели памяти, летучие записи - читать не отпускал монитор - вона имеет семантику памяти. Для того, чтобы обеспечить механизм для связи между более легким, чем блокировками монитора нитей, JSR-133 расширенной группы решила летучие семантику памяти: компилятор и процессор строго ограничен летучим переменные с нормальным переменных переупорядочением, чтобы обеспечить летучая запись - чтение и выпуск монитора - получить то же самое, имеет ту же семантику памяти. Высокое во внимание и объединение память процессора стратегия вставки барьера составлена ​​с точки зрения, до тех пор, как переупорядочения между летучей переменной и общей переменным может разрушить летучую семантику памяти, это изменение порядок будут составлены Discouraged процессора сортировки и барьер памяти вставка политики запрещается.

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

 

Опубликовано 136 оригинальных статей · вона похвала 6 · просмотров 1487

рекомендация

отblog.csdn.net/weixin_42073629/article/details/104741609