2008-09-20 8 views
470

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

Java theory and practice: Managing volatility

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

ответ

570

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

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

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

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

+3

Я согласен - я использовал volatile по той же самой причине - отслеживание, когда закончить цикл в многопоточном приложении. – 2009-06-03 10:13:44

+78

Этот ответ правильный, но неполный. Он опускает важное свойство `volatile`, которое поставляется с новой моделью памяти Java, определенной в JSR 133: когда поток читает переменную` volatile`, она видит не только значение, последнее записанное ей каким-то другим потоком, но и все другие записывают другие переменные, которые были видны в этом другом потоке во время «volatile» записи. См. [Этот ответ] (http://stackoverflow.com/questions/8769570/volatile-piggyback-is-this-enough-for-visiblity/8769692#8769692) и [эта ссылка] (http://www.ibm. ком/DeveloperWorks/библиотека/J-jtp03304/# 2,0). – 2013-07-18 12:32:53

+25

Для новичков я попросил вас продемонстрировать код (пожалуйста?) – Astrobleme 2014-02-22 06:43:38

130

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

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

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

+1

Цитата действительно приносит это к делу. – Zarathustra 2014-03-28 08:19:24

+9

Почему существуют слова `volatile` и` synchronized`? – Patrick 2015-05-09 02:32:34

+4

Статья Википедии о шаблоне Singleton сильно изменилась с тех пор и больше не содержит упомянутого «volatile». Его можно найти [в архивной версии] (https://en.wikipedia.org/w/index.php?title=Singleton_pattern&oldid=214082674#Java_5_solution). – bskp 2016-09-23 12:26:13

3

Абсолютно, да. (И не только в Java, но также и в C#.) Есть моменты, когда вам нужно получить или установить значение, которое гарантируется как атомная операция на вашей данной платформе, например int или boolean, но не требует накладные расходы на блокировку резьбы. Ключевое слово volatile позволяет вам убедиться, что при чтении значения, которое вы получаете, значение текущего, а не кешированное значение, которое было просто устарело, записано в другой поток.

+0

Исправлено. Благодаря! – dgvid 2012-01-06 14:29:00

28

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

12

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

5

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

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

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

4

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

  1. Вы должны использовать только неустойчивыми, если вы полностью понять, что он делает и как она отличается для синхронизации. Во многих ситуациях Летучие появляется, на поверхности, чтобы быть более простой более производительной альтернативой синхронизируется, когда часто лучше понимание летучего бы ясно, что синхронизируется является единственным варианта, который будет работать.
  2. volatile на самом деле не работает в много старых JVM, хотя синхронизирован. Я помню, что видел документ, который ссылался на различные уровни поддержки в разных JVM, но, к сожалению, я не могу его найти сейчас. Определенно изучите его, если вы используете Java pre 1.5 или если у вас нет контроля над JVM, над которыми будет работать ваша программа.
39

летучий очень полезный для остановки резьбы.

Не то, чтобы вы писали свои собственные потоки, 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 
    } 
} 

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

5

volatile только гарантирует, что все потоки, даже сами по себе, являются приращением. Например: счетчик видит одно и то же лицо переменной одновременно. Он не используется вместо синхронизированного или атомного или другого материала, он полностью синхронизирует чтение. Пожалуйста, не сравнивайте его с другими ключевыми словами Java. Как видно из приведенного ниже примера, операции volatile variable также являются атомарными, они терпят неудачу или преуспевают сразу.

package io.netty.example.telnet; 

import java.util.ArrayList; 
import java.util.List; 

public class Main { 

    public static volatile int a = 0; 
    public static void main(String args[]) throws InterruptedException{ 

     List<Thread> list = new ArrayList<Thread>(); 
     for(int i = 0 ; i<11 ;i++){ 
      list.add(new Pojo()); 
     } 

     for (Thread thread : list) { 
      thread.start(); 
     } 

     Thread.sleep(20000); 
     System.out.println(a); 
    } 
} 
class Pojo extends Thread{ 
    int a = 10001; 
    public void run() { 
     while(a-->0){ 
      try { 
       Thread.sleep(1); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      Main.a++; 
      System.out.println("a = "+Main.a); 
     } 
    } 
} 

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

package io.netty.example.telnet; 

    import java.util.ArrayList; 
    import java.util.List; 
    import java.util.concurrent.atomic.AtomicInteger; 

    public class Main { 

     public static volatile AtomicInteger a = new AtomicInteger(0); 
     public static void main(String args[]) throws InterruptedException{ 

      List<Thread> list = new ArrayList<Thread>(); 
      for(int i = 0 ; i<11 ;i++){ 
       list.add(new Pojo()); 
      } 

      for (Thread thread : list) { 
       thread.start(); 
      } 

      Thread.sleep(20000); 
      System.out.println(a.get()); 

     } 
    } 
    class Pojo extends Thread{ 
     int a = 10001; 
     public void run() { 
      while(a-->0){ 
       try { 
        Thread.sleep(1); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       Main.a.incrementAndGet(); 
       System.out.println("a = "+Main.a); 
      } 
     } 
    } 
9

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

  1. Double-checked locking mechanism. Часто используется в модели Singleton . В этом singleton object needs to be declared volatile.
  2. Spurious Wakeups. Поток может иногда просыпаться с ожидающего вызова, даже если не был выдан ни один из уведомлений. Такое поведение называется супервизовым пробуждением. Этому можно противопоставить использование условной переменной (булевский флаг). Поместите вызов wait() в цикл while, пока флаг будет true. Поэтому, если поток просыпается из вызова ожидания из-за каких-либо причин, кроме уведомления/notifyall, тогда он обнаруживает, что флаг по-прежнему является истинным, и, следовательно, снова вызывает ожидание. Перед вызовом уведомления установите для этого флага значение true. В этом случае boolean flag is declared as volatile.
3

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

Только переменная-член может быть изменчивой или переходной.

45

Когда есть volatile?

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

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

соображения эффективности использования volatile:

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

12

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

-1

A Изменчивая переменная модифицируется асинхронно одновременным запуском потоков в приложении Java. Не разрешается иметь локальную копию переменной, которая отличается от текущей в «основной» памяти. Эффективно переменная, объявленная volatile, должна синхронизировать свои данные по всем потокам, так что всякий раз, когда вы получаете доступ или обновляете переменную в любом потоке, все остальные потоки сразу видят одно и то же значение.Конечно, вероятно, что волатильные переменные имеют более высокий доступ и накладные расходы на обновление, чем «простые» переменные, так как потоки причин могут иметь собственную копию данных для повышения эффективности.

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

для справки см этого http://techno-terminal.blogspot.in/2015/11/what-are-volatile-variables.html

56

важного момент о летучем:

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

source

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

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; 
    } 
} 

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

Если мы не вносите переменную _instancevolatile, тогда нить, которая создает экземпляр Singleton, не может связаться с другой нитью. Поэтому, если Thread A создает экземпляр Singleton и сразу после его создания, процессор развращает и т. Д., Все остальные потоки не смогут увидеть значение _instance как не равное null, и они будут полагать, что он по-прежнему назначен 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, становятся видимыми только для других потоков, когда конструктор объекта полностью завершил выполнение.
Source

enter image description here

летучее использование в Java терпеть неудачу-быстрые итераторы обычно реализованы с использованием volatile счетчика на объекте списка.

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

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

0

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

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

1

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

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

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

Как объяснено в Peter Parker Ответ, в отсутствие модификатора volatile, каждый стек потока может иметь свою собственную копию переменной. Сделав переменную как volatile, проблемы согласованности памяти были исправлены.

Посмотрите на страницу учебника jenkov для лучшего понимания.

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

Difference between volatile and synchronized in Java

Один практический случай использования:

У вас есть много потоков, которые необходимо текущее время печати в определенном формате, например: java.text.SimpleDateFormat("HH-mm-ss"). Yon может иметь один класс, который преобразует текущее время в SimpleDateFormat и обновляет переменную каждую секунду. Все остальные потоки могут просто использовать эту переменную volatile для печати текущего времени в файлах журналов.

2

Летучие переменные - это легкая синхронизация. Когда видимость последних данных среди всех потоков является требованием, а атомарность может быть скомпрометирована, в таких ситуациях предпочтение следует отдавать переменным volatile. Чтение измененных переменных всегда возвращает самую последнюю запись, сделанную любым потоком, поскольку они не кэшируются в регистрах или в кэшах, где другие процессоры не видят. Летучие - Lock-Free. Я использую volatile, когда сценарий соответствует критериям, указанным выше.

1

Существует два варианта использования ключевого слова volatile.

  1. Запрещает JVM считывать значения из регистра (считать кешем) и заставляет его значение считывать из памяти.
  2. Снижает риск ошибок в памяти.

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

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

while (busy) { 
    /* do something else */ 
} 

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

busy = 0; 

Однако, так как занят, часто обращались в тестировании потока, й e JVM может оптимизировать тест, поместив значение занятого в регистр, а затем проверить содержимое регистра, не читая значения занятого в памяти до каждого теста. В тестовом потоке никогда не будет замечено изменение занятости, а другой поток изменит значение занятости в памяти, что приведет к тупиковой ситуации. Объявление флага занятости как изменчивого значения, которое должно быть прочитано перед каждым тестом.

Снижает риск ошибок согласованности памяти.

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

Методика считывания, записи без ошибок согласованности памяти называется атомное действие.

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

Ниже приведены действия, которые вы можете указать, что атомные:

  • чтение и записи являются атомарными для ссылочных переменных и для большинства исходных переменных (всех типов, за исключением длинного и двойного).
  • Считывания и записи являются атомными для всех объявленных переменных volatile (включая длинные и двойные переменные).

Приветствия!

0

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

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

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

Два выше качества сделать вывод, что

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

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

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

Conversly,

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