Atomic classes of JUC concurrent programming

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.atomicin 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 synchronizedlock mechanisms such as keywords, thereby improving concurrency performance.

The role of the atomic class mainly has the following points:

  1. 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.

  2. 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.

  3. 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

AtomicBooleanThe 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

AtomicIntegerand AtomicLongprovide 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

AtomicReferenceAllows manipulation of reference-type data on an atomic level. It provides methods such as get, setand .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

AtomicStampedReferenceYes AtomicReferenceextension, 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

AtomicArrayThe 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

AtomicReferenceFieldUpdaterIt 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 AtomicFieldUpdaterbut specifically for fields of reference types. AtomicReferenceFieldUpdaterIt is mainly used to ensure that operations on reference fields are thread-safe in a multi-threaded environment and can provide better performance.

AtomicReferenceFieldUpdaterApplies to the following scenarios:

  1. When you need atomic updates to reference fields of a particular class without using locks.

  2. When the access modifier of the reference field is volatile, to ensure visibility between multiple threads.

  3. 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 AtomicReferenceFieldUpdaterinstance of . This can AtomicReferenceFieldUpdater.newUpdater(Class<T> tclass, Class<V> vclass, String fieldName)be achieved by calling the method, where:

  • tclassis an object of the class containing the field Class.
  • vclassis an object of the field's reference type Class.
  • fieldNameis the name of the referenced field to be atomically operated on.

Here is an example code snippet that demonstrates how to create and use AtomicReferenceFieldUpdateran 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);
    }
}

AtomicReferenceFieldUpdaterProvides 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 to expect, update the field to update, and return whether the update is successful.

  • V getAndSet(T obj, V newValue): Update the field to newValue, 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 to xaccumulate the fields and return the value before the update.

  • V accumulateAndGet(T obj, V x, BinaryOperator<V> accumulatorFunction): Use the given accumulation function to xaccumulate the fields with and return the updated value.

7. LongAdder

LongAdderIt is a tool class provided in the Java concurrency package for accumulating long types in high concurrency scenarios. Compared with traditional ones AtomicLong, LongAdderit usually provides better performance under high concurrency because it uses a segmented approach to reduce contention. LongAdderThe introduction of is mainly to deal with the performance bottleneck of high concurrent accumulation operations, especially on multi-core processors.

LongAdderThe main advantage in high-concurrency scenarios lies in the accumulation of segments and the separate processing of hot data. In the case of traditional AtomicLonghigh concurrency, performance may be degraded due to competition among multiple threads, but LongAdderby dividing the accumulation operation into multiple segments, each segment maintains a counter, thereby reducing competition.

In addition, LongAddera 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 LongAdderinstance of:

LongAdder longAdder = new LongAdder();

 

LongAdderSome 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

  1. 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.

  2. 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.

  3. 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 AtomicStampedReferenceto solve this problem.

  4. 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 AtomicIntegerof incrementAndGetis atomic, but you still need to consider the atomicity of compound operations when using it.

  5. 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.

Guess you like

Origin blog.csdn.net/qq_43649937/article/details/132240313