Руководство по изменчивому ключевому слову в 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.