Какое ключевое слово volatile полезно для - PullRequest
595 голосов
/ 20 сентября 2008

Сегодня на работе я наткнулся на ключевое слово volatile в Java. Не очень знакомый с этим, я нашел это объяснение:

Теория и практика Java: управление волатильностью

Учитывая детали, в которых эта статья объясняет данное ключевое слово, вы когда-либо использовали его или когда-либо видели случай, когда вы могли бы использовать это ключевое слово правильным образом?

Ответы [ 23 ]

691 голосов
/ 20 сентября 2008

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

Чтобы ответить на ваш вопрос: Да, я использую переменную volatile, чтобы контролировать, продолжает ли какой-либо код цикл. Цикл проверяет значение volatile и продолжается, если оно равно true. Условие можно установить на false, вызвав метод "stop". Цикл видит false и завершается, когда он проверяет значение после того, как метод stop завершает выполнение.

Книга " Параллелизм Java на практике ", которую я настоятельно рекомендую, дает хорошее объяснение volatile. Эта книга написана тем же человеком, который написал статью IBM, на которую есть ссылка в этом вопросе (фактически, он цитирует свою книгу в нижней части этой статьи). Мое использование volatile - это то, что его статья называет «флагом состояния шаблона 1.»

Если вы хотите узнать больше о том, как volatile работает под капотом, прочтите модель памяти Java . Если вы хотите выйти за пределы этого уровня, посмотрите хорошую книгу по компьютерной архитектуре, такую ​​как Hennessy & Patterson , и прочитайте о согласованности и согласованности кеша.

162 голосов
/ 20 сентября 2008

«… модификатор volatile гарантирует, что любой поток, который читает поле, увидит последнее записанное значение». - Джош Блох

Если вы думаете об использовании volatile, прочитайте пакет java.util.concurrent, который касается атомарного поведения.

Сообщение из Википедии о Singleton Pattern показывает изменчивость в использовании.

104 голосов
/ 19 декабря 2015

Важный момент о volatile:

  1. Синхронизация в Java возможна с использованием ключевых слов Java synchronized и volatile и блокировок.
  2. В Java у нас не может быть synchronized переменной. Использование ключевого слова synchronized с переменной недопустимо и приведет к ошибке компиляции. Вместо использования переменной synchronized в Java вы можете использовать переменную java volatile, которая будет указывать потокам JVM считывать значение переменной volatile из основной памяти и не кэшировать ее локально.
  3. Если переменная не является общей для нескольких потоков, то нет необходимости использовать ключевое слово volatile.

источник

Пример использования volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

Мы создаем экземпляр лениво в момент поступления первого запроса.

Если мы не создадим переменную _instance volatile, то поток, создающий экземпляр Singleton, не сможет связаться с другим потоком. Таким образом, если поток A создает экземпляр Singleton и сразу после создания процессор испортит и т. Д., Все остальные потоки не смогут увидеть значение _instance как ненулевое, и они будут считать, что ему по-прежнему присваивается значение null.

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

Заключение : volatile Ключевое слово также используется для передачи содержимого памяти между потоками.

Пример использования без летучих:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

Код выше не является потокобезопасным. Хотя он проверяет значение экземпляра еще раз в синхронизированном блоке (по соображениям производительности), JIT-компилятор может переставить байт-код таким образом, чтобы ссылка на экземпляр была установлена ​​до того, как конструктор завершит свое выполнение. Это означает, что метод getInstance () возвращает объект, который, возможно, не был полностью инициализирован. Чтобы сделать код потокобезопасным, ключевое слово volatile может использоваться начиная с Java 5 для переменной экземпляра. Переменные, помеченные как volatile, становятся видимыми для других потоков только после того, как конструктор объекта полностью завершит свое выполнение.
Источник

enter image description here

volatile использование в Java :

Отказоустойчивые итераторы , обычно , реализованы с использованием счетчика volatile в объекте списка.

  • Когда список обновляется, счетчик увеличивается.
  • При создании Iterator текущее значение счетчика внедряется в объект Iterator.
  • Когда выполняется операция Iterator, метод сравнивает два значения счетчика и выдает ConcurrentModificationException, если они различаются.

Реализация отказоустойчивых итераторов обычно легка. Они обычно полагаются на свойства структур данных конкретной реализации списка. Там нет общей картины.

49 голосов
/ 25 сентября 2008

volatile очень полезно для остановки потоков.

Не то чтобы вы писали свои собственные потоки, в Java 1.6 есть много хороших пулов потоков. Но если вы уверены, что вам нужна нить, вам нужно знать, как ее остановить.

Шаблон, который я использую для потоков:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

Обратите внимание, что нет необходимости в синхронизации

30 голосов
/ 20 сентября 2008

Одним из распространенных примеров использования volatile является использование переменной volatile boolean в качестве флага для завершения потока. Если вы создали поток и хотите иметь возможность безопасно прервать его из другого потока, вы можете периодически проверять флаг. Чтобы остановить это, установите флаг в true. Установив флаг volatile, вы можете убедиться, что поток, который его проверяет, увидит, что он установлен, в следующий раз, когда он проверяет его, даже не используя блок synchronized.

13 голосов
/ 04 марта 2018

Переменная, объявленная с ключевым словом volatile, имеет два основных качества, которые делают ее особенной.

  1. Если у нас есть переменная volatile, она не может быть кэширована в кэш-память компьютера (микропроцессора) любым потоком. Доступ всегда происходил из основной памяти.

  2. Если существует операция записи , выполняющая переменную, и внезапно запрашивается операция , гарантируется, что операция записи будет завершена до операции чтения .

Два вышеуказанных качества выводят, что

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

А с другой стороны,

  • Если мы продолжим изучение # 2 , о котором я упоминал, мы увидим, что ключевое слово volatile является идеальным способом для поддержки разделяемой переменной, которая имеет 'n' число. из прочитанных тем и только одна запись для доступа к нему. Как только мы добавим ключевое слово volatile, все готово. Никаких других накладных расходов на безопасность потоков.

Conversly,

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

12 голосов
/ 20 сентября 2008

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

12 голосов
/ 11 февраля 2015

Никто не упомянул обработку операций чтения и записи для длинных и двойных переменных. Чтение и запись - это атомарные операции для ссылочных переменных и большинства примитивных переменных, за исключением типов переменных long и double, которые должны использовать ключевое слово volatile для атомарных операций. @ ссылка

9 голосов
/ 25 февраля 2014

По моему мнению, есть два важных сценария, отличных от остановки потока, в которых используется ключевое слово volatile:

  1. Механизм блокировки с двойной проверкой . Часто используется в дизайне Singleton шаблон. При этом одноэлементный объект должен быть объявлен как volatile .
  2. Ложные пробуждения . Поток может иногда просыпаться от ожидающего вызова, даже если не было отправлено уведомление. Такое поведение называется бестолковым пробуждением. Этому можно противостоять, используя условную переменную (логический флаг). Поместите вызов wait () в цикл while, пока флаг имеет значение true. Поэтому, если поток выходит из режима ожидания по каким-либо причинам, отличным от notify / notifyall, тогда он обнаруживает, что флаг все еще имеет значение true, и, следовательно, вызовы ожидают снова. Перед вызовом уведомления установите этот флаг в значение true. В этом случае логический флаг объявляется как volatile .
5 голосов
/ 20 сентября 2008

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

Если вы разрабатываете приложение, которое будет развернуто на сервере приложений (Tomcat, JBoss AS, Glassfish и т. Д.), Вам не придется самостоятельно управлять параллелизмом, как это уже решено сервером приложений. На самом деле, если я правильно помню, стандарт Java EE запрещает какой-либо контроль параллелизма в сервлетах и ​​EJB-компонентах, поскольку он является частью уровня «инфраструктуры», который, как вы предполагали, был освобожден от его обработки. Вы можете управлять параллелизмом только в таком приложении, если реализуете одноэлементные объекты. Это уже решено, если вы связываете свои компоненты, используя frameworkd, как Spring.

Таким образом, в большинстве случаев разработки Java, когда приложение является веб-приложением и использует IoC-фреймворк, такой как Spring или EJB, вам не нужно использовать 'volatile'.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...