2016-04-26 8 views
4

Я пытаюсь понять ключевое слово Java volatile относительно , написание изменчивой атомной переменной в многопоточной программе с кэшами процессора.Является ли Java volatile препятствием для кэширования или обеспечения сквозного кэширования?

Я прочитал несколько руководств и Специфика языка Java, в частности section 17.4.5 on "happens-before ordering". Я понимаю, что когда поток записывает новое значение в переменную volatile, обновленное значение должно быть видимым для других потоков, читающих эту переменную. Для меня эта семантика может быть реализована одним из двух способов:

  1. Потоки могут кэшировать летучий переменной в кэше процессора, но записи переменной в кэше необходимо немедленно промыть в основную память. Другими словами, кеш составляет write-through.

  2. Нити никогда не могут кэшировать изменчивую переменную и должны читать и записывать такие переменные в основной памяти.

подход 1 упоминается в этом tutorial (http://tutorials.jenkov.com), который говорит:

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

подход 2 упоминается в вопросе StackOverflow «Volatile variable in Java», а также это tutorial, который говорит:

Значение этой переменной не будет кэшировать поток локально: все читает и пишет пойдет прямо в «главную память»

Какой из них правильный подход, используемый в Java?

Похожие StackOverflow вопросы, которые делают не ответить на мой вопрос:

Volatile variable in Java

Does Java volatile read flush writes, and does volatile write update reads

Java volatile and cache coherence

+1

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

+0

Ответ SO, который вы цитируете в «Подходе 2», выглядит правильно: «« Изменчивая переменная в Java »выглядит правильно (ужины включены, и я только дал ей быстрый просмотр). Учебник, на который вы ссылаетесь в« приближении 2 », появляется – markspace

ответ

4

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

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

+0

Спасибо. Так что этот ответ неверен, правда? http://stackoverflow.com/a/6259755/4561314 «Объявление изменчивой переменной Java означает: значение этой переменной никогда не будет кэшироваться нить-локально: все чтение и запись перейдут прямо к «основной памяти». «Я думаю, что вы отвечаете, говоря, что переменные volatile могут быть кэшированы. – stackoverflowuser2010

+0

Я думаю, что у ответа есть некоторые проблемы с этим, да. –

+0

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

3

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

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


На гораздо более низком уровне, чем это касается Java; в современном оборудовании, все и все читает/пишет все адресавсегда происходит в L1 и регистрируется первым. При этом Java предназначен для скрытия такого поведения на низком уровне от программиста, поэтому это только концептуально актуально для обсуждения.

Когда мы используем ключевое слово volatile в поле Java, это просто говорит компилятору вставить что-то известное как барьер памяти при чтении/записи в это поле. Баланс памяти эффективно обеспечивает две вещи;

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

  2. Без чтения/записи ЛЮБЫЕ поля могут пересекать барьер (они всегда записываются до того, как другая нить может продолжаться, а компилятор/ООО не может переместить их в точку после барьера).

Чтобы привести простой пример Java;

//on one thread 
counter += 1; //normal int field 
flag = true; //flag is volatile 

//on another thread 
if (flag) foo(counter); //will see the incremented value 

По существу, при установке flag в true, мы создаем барьер памяти. Когда Thread # 2 пытается прочитать это поле, он попадает в наш барьер и ждет нового значения. В то же время процессор гарантирует, что counter += 1 будет записан до того, как поступит новое значение. В результате, если flag == true, то counter будет увеличено.


Итак, подведем итоги;

  1. Все потоки увидеть большинство значений последнюю дату летучих полей (которые могут быть свободно описаны как «чтение/запись пройти через совместно используемую память»).

  2. Считывание/запись в изменчивые поля устанавливаются как-либо перед отношениями с предыдущими чтениями/записью в любые поля в одном потоке.

0

Существует ли двусмысленности, хранятся volatile переменные или только не в основной памяти.

Практически подход-2, кажется, путь.

Значение этой переменной не будет кэшировать поток локально: все читает и пишет будет идти прямо к «основной памяти»

Из javabeat статьи:

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

javarevisited Из блога:

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

Посмотрите на смежные вопросы SE тоже:

Difference between volatile and synchronized in Java

Принятая ответ (от Lawrence Дол) тоже цитируемый что

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

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