Что такое атомарность в java? [дубликат]

148
27 марта 2022, 11:30
На этот вопрос уже даны ответы здесь:
Атомарные и неатомарные операции (java) (3 ответа)
Закрыт 2 года назад.

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

Например метод compareAndSet из java.util.concurrent.atomic пакета.

Но не понятно, что такое атомарность? Если атомарность - это неделимость, то приведите пример пожалуйста, чтобы стало понятно данное понятие в Java.

Answer 1

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

Мы также знаем, что все потоки процесса используют одни и те же данные:

Простой пример общей переменной x управляют два потока: A и B:

Thread A

A1. x = 5
A2. print x

Thread B

B1. x = 7

Это недетерминированный код.

Здесь нет механизма координации, поэтому мы не можем сказать, в каком порядке будут происходить эти заявления.

Некоторые возможные результаты:

5 распечатывается и является окончательным значением; 
7 распечатывается и является окончательным значением; или же
5 распечатывается, а 7 является окончательным значением.

Обратите внимание, что мы не можем распечатать 7 и получить окончательное значение 5.

Нам даже не нужно иметь несколько потоков, чтобы иметь проблему с параллелизмом. Достаточно иметь прерывания в системе. Рассмотрим приложение, которое используется для подсчета наступления какого-либо события.

Мы будем хранить количество в переменной count.

Мы предоставим пользователю возможность сброса счетчика (кнопка сброса).

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

count++; разбит на ряд более мелких операций. Если count равно 4, и мы увеличиваем его:

  1. Читаем текущее значение count (read 4)
  2. Добавляем 1 (теперь 5)
  3. Изменяем значение в памяти (write 5)

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

  1. Читаем текущее значение count (read 4)
  2. Добавляем 1 (теперь 5)
  3. INTERRUPT (обработчик прерывания)
  4. Изменяем значение в памяти (write 0)
  5. END INTERRUPT (возврат управления)
  6. Изменяем значение в памяти (write 5)

Переменная count равна 5, но она должна быть 0 (или 1). Пользователь нажал кнопку сброса, но счет не был сброшен!

Если прерывание сброса произошло до чтения переменной -> 1.

Если прерывание сброса произошло после записи переменной -> 0.

Действие сброса «потеряно».

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

Когда мы выполняем операцию, которая не может быть прервана, мы говорим, что она atomic: атомарная.

Т.е. мы хотим для определенных операций жестко установить последовательность их выполнения = атомарность.

Наиболее часто используемые атомные классы переменных в Java - AtomicInteger, AtomicLong, AtomicBoolean и AtomicReference. Эти классы представляют собой int, long, boolean и объектную ссылку соответственно, которые могут быть атомически обновлены. Основными методами, открываемыми этими классами, являются:

  • get() - получает значение из памяти, чтобы были видны изменения, внесенные другими потоками; эквивалентно чтению переменной volatile
  • set() - записывает значение в память так, чтобы изменение было видно другим потокам; эквивалентно записи переменной volatile
  • lazySet() - в конце концов записывает значение в память, может быть переупорядочено с последующими соответствующими операциями с памятью. Один из вариантов использования - обнуление ссылок, ради сбора мусора, который больше никогда не будет доступен.
  • compareAndSet() - возвращает true при успешном выполнении, в противном случае false
  • weakCompareAndSet() - Атомные классы также поддерживают метод weakCompareAndSet, который имеет ограниченную применимость. Атомно устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым значением.

Потоково безопасный пример со счетчиком AtomicInteger:

public class SafeCounterWithoutLock {
    private final AtomicInteger counter = new AtomicInteger(0);
    public int getValue() {
        return counter.get();
    }
    public void increment() {
        while(true) {
            int existingValue = getValue();
            int newValue = existingValue + 1;
            if(counter.compareAndSet(existingValue, newValue)) {
                return;
            }
        }
    }
}
READ ALSO
С чего начать изучение Spring?

С чего начать изучение Spring?

Уже несколько месяцев изучаю JavaВыучил основы языка, Core, коллекции, дженерики

124
Преобразовать Timestamp в дату

Преобразовать Timestamp в дату

В БД хранится timestamp, хочу преобразовать его в дату, но почему-то выводится дата понедельник 19 января 1970 годаЧто я не так делаю?

215
Вложенный цикл for

Вложенный цикл for

Всем приветЕсть задача Считать слово с клавиатуры и из символов вывести строки

89