Является ли изменчивый int в Java поточно-ориентированным? - PullRequest
51 голосов
/ 18 октября 2011

Является ли volatile int в Java поточно-ориентированным? То есть можно безопасно читать и записывать без блокировки?

Ответы [ 6 ]

65 голосов
/ 18 октября 2011

Да, вы можете читать с него и писать в него безопасно - но вы не можете сделать ничего сложного, например, увеличить его безопасно, так как это цикл чтения / изменения / записи.Существует также вопрос о том, как он взаимодействует с доступом к другим переменным.

Точная природа volatile откровенно сбивает с толку (подробности см. В разделе модель памяти JLS *)1006 *) - я бы лично обычно использовал бы AtomicInteger вместо этого, как более простой способ убедиться, что я правильно понял.

6 голосов
/ 18 октября 2011

[...] как можно безопасно читать и писать без блокировки?

Да, чтение всегда приведет к значению последней записи (и чтение, и запись являются атомарными операциями).

Изменчивое чтение / запись вводит в выполнение так называемое отношение «до и после».

Из спецификации языка Java Глава 17: Потоки и блокировки

Запись в энергозависимое поле (§8.3.1.4) происходит перед каждым последующим чтением этого поля.

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

Однако, как отмечает Джон Скит, использование энергозависимых переменных ограничено, и вам следует рассмотреть возможность использования классов из пакета java.util.concurrent.

2 голосов
/ 18 октября 2011

Доступ к volatile int в Java будет поточно-ориентированным.Когда я говорю доступ, я имею в виду операцию модуля над ним, например volatile_var = 10 или int temp = volatile_var (в основном запись / чтение с постоянными значениями).Ключевое слово volatile в java обеспечивает две вещи:

  1. При чтении вы всегда получаете значение в основной памяти.Как правило, в целях оптимизации JVM использует регистры или, в более общих чертах, локальная память для хранения / доступа к переменным.Таким образом, в многопоточной среде каждый поток может видеть разные копии переменных.Но делая его энергозависимым, убедитесь, что запись в переменную сбрасывается в основную память, а чтение в нее также происходит из основной памяти, и, следовательно, убедитесь, что поток видит правую копию переменной.
  2. Доступ к энергозависимости автоматически синхронизируется,Поэтому JVM обеспечивает упорядочение при чтении / записи в переменную.

Однако Джон Скит справедливо отмечает, что в неатомарных операциях (volatile_var = volatile + 1) разные потоки могут получить неожиданный результат.

0 голосов
/ 31 января 2018

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

2) В качестве альтернативы синхронизированному блоку вы также можете использовать один из множества атомарных типов данных, найденных в пакете java.util.concurrent. Например, AtomicLong или AtomicReference или один из других.

Это потокобезопасно, если у вас есть один поток записи и несколько потоков чтения.

class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}

Примечание: если помощник является неизменяемым, тогда нет необходимости использовать ключевое слово volatile. Здесь синглтон будет работать правильно.

В случае счетчика, который увеличивается на несколько потоков (операция чтения чтения), не даст правильного ответа. Это условие также проиллюстрировано расой.

public class Counter{
private volatile int i;
public int increment(){
i++;
}
}

ПРИМЕЧАНИЕ: изменчивость здесь не поможет.

0 голосов
/ 11 октября 2017

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

Предположим, у вас есть переменная i, которая является энергозависимой, и ее значение зависит от другой переменной типа скажем j.Теперь Thread-1 обращается к переменной j, увеличивает ее и собирается обновить ее в основной памяти из кэша процессора.В случае, если Thread-2 считывает переменную
i до того, как Thread-1 сможет обновить j в основной памяти.Значение i будет соответствовать старому значению j, которое будет неверным.Его также называют «грязное чтение».

0 голосов
/ 01 июля 2016

не всегда.

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

Если вы ищете Thread безопасно, используйте AtomicXXX классы

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

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

boolean compareAndSet(expectedValue, updateValue);

См. @Teto answer в посте ниже:

Летучий логический или AtomicBoolean

...