Руководство по изменчивому ключевому слову в Java

Руководство по изменчивому ключевому слову в Java

1. обзор

В этой быстрой статье мы сосредоточимся на фундаментальной, но часто неправильно понимаемой концепции языка Java - ключевом словеvolatile.

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

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

2. Когда использоватьvolatile

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

Это можно проиллюстрировать на простом примере:

public class SharedObject {
    private volatile int count = 0;

    public void increamentCount() {
        count++;
    }
    public int getCount() {
        return count;
    }
}

With no synchronization here, a typical race condition can occur. По сути, с промежутком между выполнением приращения и записью в основную память другие потоки могут увидеть значение 0 и попытаться записать его в основную память.

Конечно, состояния гонки также можно избежать с использованием предоставляемых Java атомарных типов данных, таких какAtomicInt илиAtomicLong.

3. Энергозависимая и потоковая синхронизация

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

  • Взаимное исключение - только один поток выполняет критический раздел за раз

  • Видимость - изменения, вносимые одним потоком в совместно используемые данные, видны другим потокам для обеспечения согласованности данных.

Методы и блокиSynchronized обеспечивают оба вышеуказанных свойства за счет производительности приложения.

Volatile - довольно полезный примитив, потому что этоcan help ensure the visibility aspect of the data change, without, of course, providing the mutual exclusion. Таким образом, это полезно в тех случаях, когда у нас все в порядке с несколькими потоками, выполняющими блок кода параллельно, но нам нужно обеспечить свойство видимости.

4. Произойдет до гарантии

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

Это называется Happens-Before, поскольку оно дает видимость всех переменных другому потоку чтения. Кроме того, JVM не изменяет порядок чтения и записи переменныхvolatile.

Давайте посмотрим на пример:

Thread 1
    object.aNonValitileVariable = 1;
    object.aVolatileVariable = 100; // volatile write

Thread 2:
    int aNonValitileVariable = object.aNonValitileVariable;
    int aVolatileVariable =  object.aVolatileVariable;

В этом случае, когдаThread 1 записал значениеaVolatileVariable, тогда значениеaNonValitileVariable также записывается в основную память. And even though it’s not a volatile variable, it is exhibiting a volatile behavior.

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

5. Заключение

В этом руководстве мы подробнее познакомились с ключевым словомvolatile и его возможностями, а также с улучшениями, внесенными в него, начиная с Java 5.

Как всегда, примеры кода можно найтиover on GitHub.