Как изменчивая переменная в Java влияет на ее данные при использовании неатомных операций? [Дубликат]

Конечно, есть сущность для вкладок:

	

(вкладка ASCII-символ 9 или Unicode U + 0009.)

Однако, как и буквальные вкладки (одни вы вводите текст в свой текстовый редактор), все символы табуляции обрабатываются как пропуски с помощью парсеров HTML и сворачиваются в одно пространство , за исключением тех, что находятся в блоке

 , где буквально  вкладки будут отображаться как  8 пробелов  в моноширинном шрифте. 

78
задан Ravindra babu 17 September 2016 в 17:57
поделиться

7 ответов

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

class BadExample {
    private volatile int counter;

    public void hit(){
        /* This operation is in fact two operations:
         * 1) int tmp = this.counter;
         * 2) this.counter = tmp + 1;
         * and is thus broken (counter becomes fewer
         * than the accurate amount).
         */
        counter++;
    }
}

плохой пример, потому что вам нужна сложная атомарность.

 class BadExampleFixed {
    private int counter;

    public synchronized void hit(){
        /*
         * Only one thread performs action (1), (2) at a time
         * "atomically", in the sense that other threads can not 
         * observe the intermediate state between (1) and (2).
         * Therefore, the counter will be accurate.
         */
        counter++;
    }
}

Теперь к допустимому примеру:

 class GoodExample {
    private static volatile int temperature;

    //Called by some other thread than main
    public static void todaysTemperature(int temp){
        // This operation is a single operation, so you 
        // do not need compound atomicity
        temperature = temp;
    }

    public static void main(String[] args) throws Exception{
        while(true){
           Thread.sleep(2000);
           System.out.println("Today's temperature is "+temperature);
        }
    }
}

Теперь, почему вы не можете просто используйте private static int temperature? На самом деле вы можете (в том смысле, что ваша программа не взорвется или что-то еще), но изменение к temperature другим потоком может быть или не быть «видимым» для основного потока.

В основном это означает, что даже ваше приложение возможно. продолжает писать Today's temperature is 0 навсегда, если вы не не используете volatile (на практике это значение, как правило, становится видимым. Однако при необходимости вы не должны рисковать,

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

EDIT: Ответ на этот комментарий:

< blockquote>

Хорошо, но почему мы не можем сделать todaysTemperature синхронизированы и создать синхронизированный геттер для температуры? [/ g1 2]

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

  1. Меньше ошибок: это зависит от контекста, но во многих случаях использование volatile менее подвержено ошибкам параллелизма, например блокировка, удерживая блокировку, блокировки и т. д.
  2. . Более опытный: в большинстве реализаций JVM volatile может иметь значительно более высокую пропускную способность и лучшую задержку. Однако в большинстве приложений разница слишком мала и имеет значение.
95
ответ дан Enno Shioji 16 August 2018 в 06:45
поделиться
  • 1
    Я много видел этот вопрос, и это первый ответ, который имеет для меня прекрасный смысл. Спасибо. – uyuyuy99 19 March 2014 в 01:47
  • 2
    Хорошо, но почему мы не можем синхронизировать todaysTemperature и создать синхронизированный приемник для temperature? – Semyon Danilov 21 July 2014 в 10:19
  • 3
    @SemyonDanilov: Добавлен ответ внизу. НТН. – Enno Shioji 21 July 2014 в 14:22
  • 4
    Для меня пока неясно, как здесь будет полезен синхронизированный приемник? Без использования volatile у нас по-прежнему нет гарантий относительно потокового локального кеша. – St.Antario 28 September 2015 в 07:31
  • 5
    @ St.Antario: Гарантии, предоставленные synchronized, являются надмножеством того, что предоставляет volatile, поэтому было бы излишним использовать volatile в дополнение к synchronized. – Enno Shioji 28 September 2015 в 12:29

voltalie Средство Сохраняет изменение значения. Значение этой переменной никогда не будет кэшировано нить-локально: все чтения и записи перейдут прямо к «основной памяти». Иными словами, компилятор Java и Thread, которые не кэшируют значение этой переменной и всегда читайте его из основной памяти.

2
ответ дан Akshay 16 August 2018 в 06:45
поделиться

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

Из java-параллелизма учебника :

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

< / blockquote>

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

По вашему запросу:

Как узнать, когда я должен отмечать переменную volatile? Каковы правила большого пальца при определении того, какие переменные должны быть волатильными в многопоточном коде?

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

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

Если у вас есть несколько потоков для записи и чтения переменных, модификатор volatile не гарантирует целостность памяти. Вы должны synchronize использовать код или использовать высокоуровневые параллелизм , такие как Locks, Concurrent Collections, Atomic variables и т. Д.

Связанные вопросы / статьи SE:

Объяснение переменной volatile в Java docs

Разница между летучей и синхронизированной в Java

javarevisited статья

5
ответ дан Community 16 August 2018 в 06:45
поделиться

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

Объявление поля, подобного public volatile ImmutableObject foo, гарантирует, что все потоки всегда видят доступную в настоящий момент ссылку на экземпляр.

См. Java Concurrency in Practice для получения дополнительной информации по этой теме.

5
ответ дан Johannes Wachter 16 August 2018 в 06:45
поделиться
  • 1
    Поэтому я считаю, что "неизменяемый" часть этого вопроса несколько сомнительна ... см. мой ответ здесь: stackoverflow.com/questions/3964317/… и часто задаваемые вопросы JSR-133, пункт cs.umd.edu/~ pugh / java / memoryModel / jsr-133-faq.html # volatile . Я думаю, что более точное утверждение состоит в том, что вы можете безопасно публиковать объекты, которые не требуют другой синхронизации ... Я, возможно, не так хорошо об этом думаю, но до тех пор, пока происходит изменчивая запись, нормальная запись в том же потоке, который предшествует был бы виден любому чтению нити ... – andersoj 20 October 2010 в 02:13
  • 2
    @andersoj верен, волатильность транзитивна. чтение a.b является изменчивым, так как считывание a является изменчивым. запись в a.b происходит - перед записью в a, если она предшествует ей в программном порядке. Поэтому volatile можно использовать для безопасного опубликования изменчивого (иначе не-потокового) объекта. Я не думаю, что это особенно хороший ум и всегда проповедует неизменные объекты, где это возможно, но это все еще важный момент. – Jed Wesley-Smith 21 October 2010 в 01:20

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

В JMM Cookbook описано, какие операции можно переупорядочить и какие не могут.

14
ответ дан mdma 16 August 2018 в 06:45
поделиться

http://mindprod.com/jgloss/volatile.html

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

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

2
ответ дан Molske 16 August 2018 в 06:45
поделиться

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

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

. Лучший способ проиллюстрировать изменчивую семантику - это две переменные.

Для простоты будем считать, что единственный способ обновить две переменные - через метод setTemperatures.

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

//volatile variable
private static volatile int temperature; 
//any other variable, could be volatile or not volatile doesnt matter.
private static int yesterdaysTemperature
//Called by other thread(s)
public static void setTemperatures(int temp, int yestemp){
    //thread updates yesterday's temperature
    yesterdaysTemperature = yestemp;
    //thread updates today's temperature. 
    //This instruction can NOT be moved above the previous instruction for optimization.
    temperature = temp;
   }

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

public static void main(String[] args) throws Exception{
    while(true){
       Thread.sleep(2000);
       System.out.println("Today's temperature is "+temperature); 
       System.out.println("Yesterday's temperature was "+yesterdaysTemperature );
 }
}

Когда основной поток считывает изменчивую температуру переменной (в процессе ее печати),

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

2) Если оператор system.out в основном потоке запускается, после момент времени при котором поток 2 запускает инструкцию temperature = temp, как вчерашняя температура, так и сегодняшняя температура будут гарантированно печатать значения, установленные в них потоком 2 w При выполнении этой задачи температура = temp.

Эта ситуация становится более сложной, если: a) выполняется несколько потоков и b) существуют другие методы, кроме метода setTemperatures, который может обновлять переменную вчерашнюю температуру и текущую температуру, которые активно вызывают эти другие потоки. Я думаю, что для анализа последствий, основанных на том, как модель памяти Java описывает изменчивую семантику, потребуется статья с приличным размером.

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

3
ответ дан programmerravi 16 August 2018 в 06:45
поделиться
  • 1
    Ваш пример может также служить объяснением, где использование синхронизации может быть лучше. Эти операторы печати могут выполняться, пока другой поток выполнил & quot; yesterdaysTemperature = yestemp & quot; но еще не выполнена «температура = температура», не может ли это быть? – EricS 18 May 2016 в 14:18
Другие вопросы по тегам:

Похожие вопросы: