Пример ошибки согласованности памяти при использовании ключевого слова volatile?

Из документов:

Использование volatile-переменных снижает риск ошибок согласованности памяти.

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


person user710818    schedule 28.11.2012    source источник
comment
Я настоятельно рекомендую вам ознакомиться с параллелизмом Java Брайана Гетца на практике. Это на Amazon: amazon.com/Java-Concurrency-Practice- Брайан-Гетц/dp/0321349601/   -  person Cameron Skinner    schedule 28.11.2012
comment
Это превосходная книга, которая проясняет почти все, что связано с моделями памяти и многопоточности в Java.   -  person Cameron Skinner    schedule 28.11.2012


Ответы (2)


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

Переменная volatile всегда будет распространять любые записи на все потоки. Однако предположим, что вам нужно увеличить переменную среди различных потоков. Делаем это(*):

volatile int mCounter;

// later, in some code that might be executed simultaneously on multiple threads:
mCounter++;

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

Для получения дополнительной информации о volatile и synchronized я рекомендую статью Управление волатильностью Брайан Гетц

(*) Я понимаю, что вышеописанное было бы лучше реализовать с помощью AtomicInteger; это надуманный пример, чтобы проиллюстрировать точку зрения.

person Ted Hopp    schedule 28.11.2012

Volatile выполняет следующие действия:

- предотвращает кэширование значений в потоке.

- Обеспечивает согласование потоков, имеющих копии значений полей объекта, с основной копией, находящейся в памяти.

- Убедитесь, что данные записываются непосредственно в память и считываются из самой памяти.

## Но условие, при котором volatile не работает:

- Создание Non-Atomic statement Volatile.

Например:

int count = 0;

count++;  // Increment operator is Not Atomic in java

## Лучший вариант:

1. Всегда лучше следовать Brian's Rule:

When ever we write a variable which is next to be read by another thread, or when we are reading a variable which is written just by another thread, it needs to be synchronized. The shared fields must be made private, making the read and write methods/atomic statements synchronized.

2. Второй вариант — использовать Atomic Classes, например AtomicInteger, AtomicLong, AtomicReference и т. д.

## См. эту ссылку, я задал вопрос, похожий на ваш:

Почему Volatile ведет себя странно

person Kumar Vivek Mitra    schedule 28.11.2012