Пакет java.util.concurrent.atomic

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

boolean compareAndSet(expectedValue, updateValue); 

Этот метод (который различается по типам аргументов в разных классах) атомарно устанавливает переменную updateValue, если она в настоящее время содержит ожидаемое значение, возвращая true в случае успеха. Классы в этом пакете также содержат методы для получения и безоговорочной установки значений, а также более слабую операцию условного атомарного обновления weakCompareAndSet.

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

Каждый из экземпляров классов AtomicBoolean, AtomicInteger, AtomicLong и AtomicReference обеспечивает доступ и обновления к одной переменной соответствующего типа. Каждый класс также предоставляет соответствующие служебные методы для этого типа. Например, классы AtomicLong и AtomicInteger предоставляют методы атомарного приращения. Одно из применений - создание порядковых номеров, например:

class Sequencer {
    private final AtomicLong sequenceNumber = new AtomicLong(0);
    public long next() {
        return sequenceNumber.getAndIncrement();
    }
}

Несложно определить новые служебные функции, которые, как и getAndIncrement, атомарно применяют функцию к значению. Например, учитывая некоторую трансформацию

long transform(long input)

можно написать следующее:

long getAndTransform(AtomicLong var) {
    long prev, next;
    do {
      prev = var.get();
      next = transform(prev);
    } while (!var.compareAndSet(prev, next));
    return prev; // return next; для transformAndGet
}

Эффекты памяти для доступа и обновления атомарных переменных обычно соответствуют правилам для volatile, как указано в Спецификации языка Java (модель памяти 17.4):

  • get имеет эффекты памяти при чтении volatile переменной.
  • set имеет эффекты памяти записи (назначения) volatile переменной.
  • lazySet имеет эффекты памяти записи (назначения) volatile переменной, за исключением того, что он позволяет переупорядочивать с последующими (но не предыдущими) действиями с памятью, которые сами по себе не накладывают ограничений переупорядочения с обычными не-volatile записями. Среди других контекстов использования lazySet может применяться при обнулении для сбора мусора ссылки, к которой больше никогда не будет доступа.
  • weakCompareAndSet атомарно считывает и записывает переменную по условию, но не создает каких-либо упорядоченных операций (happens-before orderings), поэтому не дает никаких гарантий относительно предыдущих или последующих операций чтения и записи любых переменных, кроме цели weakCompareAndSet.
  • compareAndSet и все другие операции чтения и обновления, такие как getAndIncrement, имеют эффекты памяти как чтения, так и записи volatile переменных.

В дополнение к классам, представляющим отдельные значения, этот пакет содержит классы Updater, которые можно использовать для получения операций compareAndSet в любом выбранном volatile поле любого выбранного класса. AtomicReferenceFieldUpdater, AtomicIntegerFieldUpdater и AtomicLongFieldUpdater - это утилиты на основе отражения, которые обеспечивают доступ к связанным типам полей. В основном они используются в атомарных структурах данных, в которых несколько volatile полей одного и того же узла (например, ссылки узла дерева) независимо подвергаются атомарным обновлениям. Эти классы обеспечивают большую гибкость в том, как и когда использовать атомарные обновления, за счет более неуклюжей настройки на основе отражения, менее удобного использования и более слабых гарантий.

Классы AtomicIntegerArray, AtomicLongArray и AtomicReferenceArray дополнительно расширяют поддержку атомарных операций для массивов этих типов. Эти классы также примечательны тем, что предоставляют семантику volatile доступа для своих элементов массива, которая не поддерживается для обычных массивов.

Атомарные классы также поддерживают метод weakCompareAndSet, который имеет ограниченную применимость. На некоторых платформах weak версия может быть более эффективной, чем compareAndSet в обычном случае, но отличается тем, что любой конкретный вызов метода weakCompareAndSet может ложно возвращать false значение (то есть без видимой причины). Возврат false означает только то, что операцию можно повторить при желании, полагаясь на гарантию того, что повторный вызов, когда переменная содержит ожидаемое значение и ни один другой поток также не пытается установить переменную, в конечном итоге будет успешным. (Такие ложные сбои могут быть, например, из-за эффектов конкуренции за память, которые не связаны с тем, равны ли ожидаемые и текущие значения.) Кроме того, weakCompareAndSet не обеспечивает гарантии упорядочения, которые обычно необходимы для управления синхронизацией. Однако этот метод может быть полезен для обновления счетчиков и статистики, когда такие обновления не связаны с другими событиями, происходящими до упорядочения программы. Когда поток видит обновление атомарной переменной, вызванное weakCompareAndSet, он не обязательно видит обновления любых других переменных, которые произошли до weakCompareAndSet. Это может быть приемлемо, например, при обновлении статистики производительности, но редко в противном случае.

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

Атомарные классы разработаны в первую очередь как строительные блоки для реализации неблокирующих структур данных и связанных классов инфраструктуры. Метод compareAndSet не является общей заменой блокировки. Он применяется только тогда, когда критические обновления объекта ограничиваются одной переменной.

Атомарные классы не являются заменой общего назначения для java.lang.Integer и связанных классов. Они не определяют такие методы, как equals, hashCode и compareTo. (Поскольку ожидается, что атомарные переменные будут видоизменяться, они не подходят для ключей хэш-таблицы.) Кроме того, классы предоставляются только для тех типов, которые обычно используются в предполагаемых приложениях. Например, не существует атомарного класса для представления байта. В тех редких случаях, когда вы хотели бы это сделать, вы можете использовать AtomicInteger для хранения байтовых значений и соответствующего преобразования. Вы также можете хранить float, используя преобразования Float.floatToRawIntBits(float) и Float.intBitsToFloat(int), и double с помощью преобразований Double.doubleToRawLongBits(double) и Double.longBitsToDouble(long).


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки