Table of contents
1. What is an atomic operation
1.1 The role of atomic classes
1.2 Common operations of atomic classes
Notes on the use of atomic classes
Concurrent programming is an indispensable part of modern computer applications, and in concurrent programming, dealing with concurrent access to shared resources is an important issue. In order to avoid problems such as race conditions when multiple threads access shared resources, Java provides a set of atomic classes (Atomic Classes) to support thread-safe operations.
1. What is an atomic operation
In concurrent programming, an atomic operation is one or a series of operations that cannot be interrupted. Either all of them are executed successfully, or none of them are executed, and there will be no partial execution. Atomic operations can ensure that operations on shared resources will not interfere with each other in a multi-threaded environment, thereby ensuring data consistency and reliability.
1.1 The role of atomic classes
Java provides a set of atomic classes, located java.util.concurrent.atomic
in the package, for atomic operations in a multi-threaded environment. These atomic classes use underlying hardware support or mechanisms such as spin locks to implement thread-safe operations, avoiding the explicit use of synchronized
lock mechanisms such as keywords, thereby improving concurrency performance.
The role of the atomic class mainly has the following points:
-
Provide thread-safe operations: Atomic classes provide some common operations, such as read, update, compare and exchange, etc. These operations will not be interfered by other threads during execution, thereby ensuring data consistency.
-
Avoid race conditions: Using atomic classes can effectively avoid race conditions in a multi-threaded environment. For example, multiple threads operate on the same variable at the same time, which may lead to unpredictable results.
-
Improve performance: The atomic class utilizes some underlying technologies to avoid the overhead of the traditional lock mechanism, so it can provide better performance in some cases.
1.2 Common operations of atomic classes
1. AtomicBoolean
AtomicBoolean
The class provides atomic Boolean operations and supports atomic set and get operations.
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
boolean currentValue = atomicBoolean.get(); // 获取当前值
boolean updatedValue = atomicBoolean.compareAndSet(true, false); // 如果当前值为true,则设置为false
2. AtomicInteger 和 AtomicLong
AtomicInteger
and AtomicLong
provide atomic integer and long integer operations, including operations such as increase, decrease, and get, respectively.
AtomicInteger atomicInt = new AtomicInteger(0);
int currentValue = atomicInt.get(); // 获取当前值
int newValue = atomicInt.incrementAndGet(); // 增加1并返回新值
int updatedValue = atomicInt.addAndGet(5); // 增加5并返回新值
3. AtomicReference
AtomicReference
Allows manipulation of reference-type data on an atomic level. It provides methods such as get
, set
and .compareAndSet
AtomicReference<String> atomicRef = new AtomicReference<>("initial value");
String currentValue = atomicRef.get(); // 获取当前值
boolean updated = atomicRef.compareAndSet("initial value", "new value"); // 如果当前值为"initial value",则设置为"new value"
4. AtomicStampedReference
AtomicStampedReference
Yes AtomicReference
extension, it also includes a timestamp, which is used to solve the ABA problem (i.e. one value was modified to another value, then changed back to the original value, but other changes may have happened in between).
AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>("initial value", 0);
int currentStamp = atomicStampedRef.getStamp(); // 获取当前时间戳
String currentValue = atomicStampedRef.getReference(); // 获取当前值
boolean updated = atomicStampedRef.compareAndSet("initial value", "new value", 0, 1); // 如果当前值为"initial value"且时间戳为0,则设置为"new value"和时间戳为1
5. AtomicArray
AtomicArray
The class allows manipulation of array elements at the atomic level, providing atomic update operations on array elements.
AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(5);
int currentValue = atomicIntArray.get(2); // 获取索引为2的元素值
atomicIntArray.set(3, 10); // 设置索引为3的元素值为10
int updatedValue = atomicIntArray.getAndAdd(1, 5); // 增加索引为1的元素值,并返回旧值
6. AtomicReferenceFieldUpdater
AtomicReferenceFieldUpdater
It is a tool class in Java, which is used to atomically update the reference type field of the class. It allows you to perform atomic operations on specified reference fields without using locks, similar to AtomicFieldUpdater
but specifically for fields of reference types. AtomicReferenceFieldUpdater
It is mainly used to ensure that operations on reference fields are thread-safe in a multi-threaded environment and can provide better performance.
AtomicReferenceFieldUpdater
Applies to the following scenarios:
-
When you need atomic updates to reference fields of a particular class without using locks.
-
When the access modifier of the reference field is
volatile
, to ensure visibility between multiple threads. -
When you want the ability to atomically update a referenced field shared between multiple instances, rather than the entire object.
To use AtomicReferenceFieldUpdater
, you first need to create an AtomicReferenceFieldUpdater
instance of . This can AtomicReferenceFieldUpdater.newUpdater(Class<T> tclass, Class<V> vclass, String fieldName)
be achieved by calling the method, where:
tclass
is an object of the class containing the fieldClass
.vclass
is an object of the field's reference typeClass
.fieldName
is the name of the referenced field to be atomically operated on.
Here is an example code snippet that demonstrates how to create and use AtomicReferenceFieldUpdater
an instance:
public class AtomicReferenceFieldUpdaterExample {
public static class Student {
public volatile String name;
}
public static void main(String[] args) {
Student student = new Student();
AtomicReferenceFieldUpdater<Student, String> updater =
AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
updater.set(student, "Alice"); // 原子地设置name字段为"Alice"
String updatedName = updater.get(student); // 原子地获取name字段的值
System.out.println("Updated Name: " + updatedName);
}
}
AtomicReferenceFieldUpdater
Provides a series of atomic operation methods for atomic update of specified reference fields. These methods include:
-
boolean compareAndSet(T obj, V expect, V update)
: If the current value is equal toexpect
, update the field toupdate
, and return whether the update is successful. -
V getAndSet(T obj, V newValue)
: Update the field tonewValue
, and return the previous value. -
V getAndUpdate(T obj, UnaryOperator<V> updateFunction)
: Use the given update function to update the field and return the value before update. -
V updateAndGet(T obj, UnaryOperator<V> updateFunction)
: Update the field with the given update function and return the updated value. -
V getAndAccumulate(T obj, V x, BinaryOperator<V> accumulatorFunction)
: Use the given accumulation function tox
accumulate the fields and return the value before the update. -
V accumulateAndGet(T obj, V x, BinaryOperator<V> accumulatorFunction)
: Use the given accumulation function tox
accumulate the fields with and return the updated value.
7. LongAdder
LongAdder
It is a tool class provided in the Java concurrency package for accumulating long types in high concurrency scenarios. Compared with traditional ones AtomicLong
, LongAdder
it usually provides better performance under high concurrency because it uses a segmented approach to reduce contention. LongAdder
The introduction of is mainly to deal with the performance bottleneck of high concurrent accumulation operations, especially on multi-core processors.
LongAdder
The main advantage in high-concurrency scenarios lies in the accumulation of segments and the separate processing of hot data. In the case of traditional AtomicLong
high concurrency, performance may be degraded due to competition among multiple threads, but LongAdder
by dividing the accumulation operation into multiple segments, each segment maintains a counter, thereby reducing competition.
In addition, LongAdder
a mechanism called "Splitter" (Cell) was introduced. A separator is the basic unit of a counter, and each thread will select a separator to operate when accumulating, which avoids frequent competition among multiple threads for the same counter, thereby reducing the overhead caused by competition.
To use LongAdder
, simply create an LongAdder
instance of:
LongAdder longAdder = new LongAdder();
LongAdder
Some common methods are provided to perform accumulation operations:
-
void add(long x)
: Add the specified value to the counter. -
void increment()
: Increment the counter by 1. -
void decrement()
: Decrements the counter by 1. -
long sum()
: Returns the sum of the current counter. -
void reset()
: Reset the counter to 0. -
void addThenReset(long x)
: Adds the specified value to the counter, then resets the counter to 0.
Notes on the use of atomic classes
-
Performance considerations: While atomic classes can provide some degree of performance benefit, they are not suitable for all situations. In high concurrency scenarios, consider using atomic classes; in low concurrency and low performance requirements, traditional synchronization mechanisms may be more appropriate.
-
Restrictions on CAS operations: The underlying implementation of atomic classes mainly relies on CAS (Compare-And-Swap) operations, which is an optimistic locking mechanism. However, CAS operations may cause spin waits under high contention, affecting performance.
-
ABA problem: The CAS operation of the atomic class may have an ABA problem, that is, a value changes from A to B, and then from B to A. At this time, the CAS operation may mistakenly believe that the value has not changed. can be used
AtomicStampedReference
to solve this problem. -
Atomicity of composite operations: A single operation of an atomic class is atomic, but a combination of multiple operations is not necessarily atomic. For example, the operation
AtomicInteger
ofincrementAndGet
is atomic, but you still need to consider the atomicity of compound operations when using it. -
Scope of application: Atomic classes are suitable for simple atomic operations, but not for complex business logic. For complex operations, more advanced synchronization mechanisms such as locks may be required to ensure thread safety.