Какие есть атомарные типы java

Package java.util.concurrent.atomic

A reflection-based utility that enables atomic updates to designated volatile int fields of designated classes.

A reflection-based utility that enables atomic updates to designated volatile long fields of designated classes.

An AtomicMarkableReference maintains an object reference along with a mark bit, that can be updated atomically.

A reflection-based utility that enables atomic updates to designated volatile reference fields of designated classes.

An AtomicStampedReference maintains an object reference along with an integer «stamp», that can be updated atomically.

One or more variables that together maintain a running double value updated using a supplied function.

Package java.util.concurrent.atomic Description

A small toolkit of classes that support lock-free thread-safe programming on single variables. In essence, the classes in this package extend the notion of volatile values, fields, and array elements to those that also provide an atomic conditional update operation of the form:

 boolean compareAndSet(expectedValue, updateValue);

This method (which varies in argument types across different classes) atomically sets a variable to the updateValue if it currently holds the expectedValue , reporting true on success. The classes in this package also contain methods to get and unconditionally set values, as well as a weaker conditional atomic update operation weakCompareAndSet described below.

The specifications of these methods enable implementations to employ efficient machine-level atomic instructions that are available on contemporary processors. However on some platforms, support may entail some form of internal locking. Thus the methods are not strictly guaranteed to be non-blocking — a thread may block transiently before performing the operation.

Instances of classes AtomicBoolean , AtomicInteger , AtomicLong , and AtomicReference each provide access and updates to a single variable of the corresponding type. Each class also provides appropriate utility methods for that type. For example, classes AtomicLong and AtomicInteger provide atomic increment methods. One application is to generate sequence numbers, as in:

It is straightforward to define new utility functions that, like getAndIncrement , apply a function to a value atomically. For example, given some transformation

 long getAndTransform(AtomicLong var) < long prev, next; do < prev = var.get(); next = transform(prev); >while (!var.compareAndSet(prev, next)); return prev; // return next; for transformAndGet >
  • get has the memory effects of reading a volatile variable.
  • set has the memory effects of writing (assigning) a volatile variable.
  • lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non- volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.
  • weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet .
  • compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

Submit a bug or feature
For further API reference and developer documentation, see Java SE Documentation. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.
Copyright © 1993, 2023, Oracle and/or its affiliates. All rights reserved. Use is subject to license terms. Also see the documentation redistribution policy.

Источник

Атомарные операции в Java

Давай разберем данный пример, который поможет понять работу атомарных операций:

Когда у нас один поток, все работает классно, но если мы добавляем многопоточку, то получаем неправильные результаты, а все из-за того, что операция инкремента составляет не одну операцию, а три: запрос на получение текущего значения count , потом увелечение ее на 1 и запись снова в count .

И когда два потока захотят увеличить переменную, скорее всего, ты потеряешь данные. То есть оба потока получают 100, в результате оба запишут 101 вместо ожидаемого значения 102.

И как же это решить? Нужно использовать блокировки. Ключевое слово synchronized помогает решить данную проблему, использование этого слова дает вам гарантию, что один поток будет обращаться к методу единовременно.

 public class SynchronizedCounterWithLock < private volatile int count; public synchronized void increment() < count++; >> 

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

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

Все эти процессы, блокировка, переход в другой статус ​​— очень дороги для производительности системы.

Атомарные операции

Алгоритм использует низкоуровневые машинные инструкции, такие как сравнение и замена (CAS, compare-and-swap, что обеспечивает целостность данных и по ним уже существует большое количество исследований).

Типичная операция CAS работает с тремя операндами:

  • Место в памяти для работы (M)
  • Существующее ожидаемое значение (A) переменной
  • Новое значение (B), которое необходимо установить

CAS атомарно обновляет M до B, но только если значение M совпадает с A, в противном случае никаких действий предприниматься не будет.

В первом и втором случае вернут значение М. Это позволяет объединить три шага, а именно — получение значения, сравнение значения и его обновление. И это все превращается в одну операцию на машинном уровне.

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

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

Знакомство с атомарными типами

Ты столкнулся с ситуацией, когда тебе нужно настроить синхронизацию для самой простой переменной типа int ?

Первый способ, который мы уже разобрали – это использование volatile + synchronized . Но есть еще специальные классы Atomic*.

Если у нас используется CAS, то операции работают быстрее по сравнению с первым способом. И в дополнение у нас есть специальные и очень удобные методы для добавления значения и операции инкремента и декремента.

AtomicBoolean , AtomicInteger , AtomicLong , AtomicIntegerArray , AtomicLongArray —классы в которых операции атомарны. Ниже мы разберем работу с ними.

AtomicInteger

Класс AtomicInteger предоставляет операции с значением int , которые могут быть прочитаны и записаны атомарно, в дополнение содержит расширенные атомарные операции.

У него есть методы get и set , которые работают, как чтение и запись по переменным.

То есть “происходит до (happens-before)” с любым последующим получением той же переменной, о которой мы говорили ранее. У атомарного метода compareAndSet также есть эти особенности согласованности памяти.

Все операции, которые возвращают новое значение, выполняются атомарно:

int addAndGet (int delta) Добавляет определенное значение к текущему значению.
boolean compareAndSet (ожидаемое int, обновление int) Устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым значением.
int decrementAndGet () Уменьшает на единицу текущее значение.
int getAndAdd (int delta) Добавляет данное значение к текущему значению.
int getAndDecrement () Уменьшает на единицу текущее значение.
int getAndIncrement () Увеличивает на единицу текущее значение.
int getAndSet (int newValue) Устанавливает заданное значение и возвращает старое значение.
int incrementAndGet () Увеличивает на единицу текущее значение.
lazySet (int newValue) В конце-концов устанавливается на заданное значение.
boolean weakCompareAndSet (ожидаемое, обновление int) Устанавливает значение для данного обновленного значения, если текущее значение совпадает с ожидаемым значением.
 ExecutorService executor = Executors.newFixedThreadPool(5); IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet)); executor.shutdown(); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS); System.out.println(atomicInteger.get()); // выведет 50 

Источник

Atomic Variables

The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables.

To see how this package might be used, let’s return to the Counter class we originally used to demonstrate thread interference:

class Counter < private int c = 0; public void increment() < c++; >public void decrement() < c--; >public int value() < return c; >>

One way to make Counter safe from thread interference is to make its methods synchronized, as in SynchronizedCounter :

class SynchronizedCounter < private int c = 0; public synchronized void increment() < c++; >public synchronized void decrement() < c--; >public synchronized int value() < return c; >>

For this simple class, synchronization is an acceptable solution. But for a more complicated class, we might want to avoid the liveness impact of unnecessary synchronization. Replacing the int field with an AtomicInteger allows us to prevent thread interference without resorting to synchronization, as in AtomicCounter :

import java.util.concurrent.atomic.AtomicInteger; class AtomicCounter < private AtomicInteger c = new AtomicInteger(0); public void increment() < c.incrementAndGet(); >public void decrement() < c.decrementAndGet(); >public int value() < return c.get(); >>

Источник

Читайте также:  Network unavailable java net unknownhostexception repo1 maven org
Оцените статью