Летучие Vs Atomic

Я читаю где-то ниже строки.

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

Итак, что произойдет, если два потока атакуют примитивную переменную volatile в одно и то же время?

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

В чем разница между ключевым словом Atomic и volatile?

Ответ 1

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

Примечательно, однако, что операция, которая требует более одного чтения/записи - например, i++, который эквивалентен i = i + 1, который делает одно чтение и один - не является атомарным, поскольку другой поток может напишите в i между чтением и записью.

Классы Atomic, такие как AtomicInteger и AtomicReference, предоставляют более широкий спектр операций атомарно, в частности, включая приращение для AtomicInteger.

Ответ 2

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

Возьмем следующий пример двух потоков в Java:

Тема А:

value = 1;
done = true;

Тема B:

if (done)
  System.out.println(value);

Начиная с value = 0 и done = false правило потоковой передачи сообщает нам, что это undefined, будет ли Thread B печатать значение. Кроме того, значение undefined в этой точке также!. Чтобы объяснить это, вам нужно немного узнать о управлении памятью Java (что может быть сложным), вкратце: потоки могут создавать локальные копии переменных, и JVM может изменить порядок кода для его оптимизации, поэтому нет гарантии, что вышеуказанный код будет выполняться именно в этом порядке. Значение, установленное для значения true и then, равное 1, может быть возможным результатом оптимизации JIT.

volatile только гарантирует, что в момент доступа такой переменной новое значение будет сразу видно всем остальным потокам и, порядок выполнения гарантирует, что код находится в государство, которого вы ожидаете. Поэтому в случае вышеприведенного кода определение done как volatile будет гарантировать, что всякий раз, когда Thread B проверяет переменную, она либо ложна, либо истинна, и если она истинна, то value также установлено на 1.

В качестве побочного эффекта волатильности значение такой переменной задается по потоку атоматически (при очень незначительной стоимости скорости выполнения). Однако это важно только для 32-битных систем, которые i.E. использовать длинные (64-битные) переменные (или аналогичные), в большинстве других случаев установка/чтение переменной является атомной в любом случае. Но есть существенное различие между атомарным доступом и атомной операцией. Volatile только гарантирует, что доступ является атомарным, в то время как Atomics гарантирует, что операция будет атомарной.

Возьмем следующий пример:

i = i + 1;

Независимо от того, как вы определяете i, другой поток, считывающий значение только тогда, когда выполняется эта строка, может получить я или я + 1, поскольку операция не является атомарной. Если другой поток устанавливает я в другое значение, в худшем случае я мог бы вернуться к тому, что было раньше, по потоку A, потому что он был только посреди вычисления я + 1 на основе старого значения, а затем установите я снова к этому старому значению + 1. Объяснение:

Assume i = 0
Thread A reads i, calculates i+1, which is 1
Thread B sets i to 1000 and returns
Thread A now sets i to the result of the operation, which is i = 1

Атомы, такие как AtomicInteger, гарантируют, что такие операции происходят атомарно. Таким образом, вышеупомянутая проблема не может произойти, я бы или 1000 или 1001 после завершения обоих потоков.

Ответ 3

В многопоточной среде есть два важных понятия.

  • атомарность
  • видимость

Volatile устраняет проблему видимости, но не имеет дело с атомарностью. Volatile не позволит компилятору переупорядочить инструкцию, которая включает в себя запись и последующее чтение изменчивой переменной. например k++ Здесь k++ - это не одна машинная инструкция, а три машинные команды.

  • скопировать значение для регистрации
  • увеличить его
  • разместите его

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

Но AtomicInteger, AtomicReference основаны на инструкции Сравнение и свопинг. CAS имеет три операнда: расположение памяти V для работы, ожидаемое старое значение A и новое значение B. CAS Atomically обновляет V до нового значения B, но только если значение в V соответствует ожидаемому старому значению A; иначе он ничего не делает. В любом случае он возвращает значение в настоящее время в V. Это используется JVM в AtomicInteger, AtomicReference, и они вызывают функцию как compareAndSet(). Если эта функциональность не поддерживается базовым процессором, то JVM реализует ее с помощью блокировки блокировки.

Ответ 4

Как указано, volatile имеет дело только с видимостью.

Рассмотрим этот фрагмент в параллельной среде:

boolean isStopped = false;
    :
    :

    while (!isStopped) {
        // do some kind of work
    }

Идея здесь в том, что какой-то поток может изменить значение isStopped от false до true, чтобы указать последующему циклу, что настало время прекратить цикл.

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

Причина этого не является интуитивной, но считают, что современные процессоры имеют несколько ядер и что каждое ядро ​​имеет несколько регистров и несколько уровней кэш-памяти, которые недоступны для других процессоров. Другими словами, значения, которые кэшируются в локальной памяти процессора, не visisble для потоков, выполняемых на другом процессоре. В этом заключается одна из центральных проблем с concurrency: видимость.

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

Ключевое слово volatile - это слабая форма синхронизации. Хотя он ничего не делает для взаимного исключения или атомарности, он обеспечивает гарантию того, что изменения, внесенные в переменную в один поток, станут видимыми для других потоков, как только они будут сделаны. Поскольку отдельные чтения и записи переменных, которые не являются 8 байтами, являются атомарными в Java, объявление переменных volatile обеспечивает простой механизм обеспечения видимости в ситуациях, когда нет других требований к атомичности или взаимному исключению.

Ответ 5

Используется ключевое слово volatile:

  • сделать неатомные 64-битные операции атомарными: long и double. (все остальные, примитивные обращения уже гарантированно являются атомарными!)
  • чтобы гарантировать, что переменные обновления гарантированы для просмотра другими потоками + эффекты видимости: после записи в переменную volatile все переменные, которые видны до записи этой переменной, становятся видимыми для другого потока после прочтения одной и той же изменчивой переменной (раньше упорядочение).

Классы java.util.concurrent.atomic.*, согласно java docs:

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

boolean compareAndSet(expectedValue, updateValue);

Атомные классы построены вокруг атомной compareAndSet(...) функции, которая сопоставляется с инструкцией атомного процессора. Атомные классы вводят порядок событий, как это делают переменные volatile. (за одним исключением: weakCompareAndSet(...)).

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

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

На ваш вопрос:

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

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

В чем разница между AtomicInteger и volatile int?

AtomicInteger обеспечивает атомарные операции с int с надлежащей синхронизацией (например, incrementAndGet(), getAndAdd(...),...), volatile int просто обеспечит видимость int для других потоков.

Ответ 6

Итак, что произойдет, если два потока одновременно будут атаковать изменчивую примитивную переменную?

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

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

Нет блокировки. Для этого используется synchronized.

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

Да,

В чем разница между ключевым словом Atomic и volatile?

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

AtomicXxxx также поддерживает lazySet. Это похоже на энергозависимый набор, но не останавливает трубопровод, ожидающий завершения записи. Это может означать, что если вы прочитаете значение, которое вы просто напишете, вы можете увидеть старое значение, но вы все равно не должны этого делать. Разница в том, что установка volatile занимает около 5 нс, бит lazySet занимает около 0,5 нс.