Appearance
Atomic Operations
In addition to providing low-level locks and concurrent collections, Java's java.util.concurrent
package also includes a set of atomic operation wrapper classes located in the java.util.concurrent.atomic
package.
Using AtomicInteger
as an example, the main operations it provides are:
- Increment by a specified value and return the new value:
int addAndGet(int delta)
- Increment by 1 and return the new value:
int incrementAndGet()
- Get the current value:
int get()
- Set a value using Compare-and-Swap (CAS):
boolean compareAndSet(int expect, int update)
The atomic classes achieve thread-safe access using a lock-free approach. The core principle behind this is CAS (Compare and Set).
How CAS Works
If we were to implement incrementAndGet()
using CAS manually, it might look something like this:
java
public int incrementAndGet(AtomicInteger var) {
int prev, next;
do {
prev = var.get();
next = prev + 1;
} while (!var.compareAndSet(prev, next));
return next;
}
In this code, CAS checks if the current value of AtomicInteger
is prev
. If it is, the value is updated to next
, and the operation returns true
. If the current value is not prev
, nothing happens, and the operation returns false
. By using CAS with a do ... while
loop, even if other threads modify the value of AtomicInteger
, the final result remains correct.
Example: Generating Unique IDs
Using AtomicLong
, we can implement a thread-safe global unique ID generator:
java
class IdGenerator {
AtomicLong var = new AtomicLong(0);
public long getNextId() {
return var.incrementAndGet();
}
}
Usually, we don't need to use do ... while
loops and compareAndSet
directly for complex concurrent operations. Instead, we can use built-in methods like incrementAndGet()
, making it easy to work with atomic classes.
Advanced Atomic Operations: LongAdder and LongAccumulator
In high contention scenarios, Java 8 introduced LongAdder
and LongAccumulator
for better performance. These classes are optimized for situations where there are frequent updates, such as counters and accumulators.
Summary
Using atomic operations provided by java.util.concurrent.atomic
simplifies multithreading programming:
- Atomic operations implement thread safety without using locks.
- Suitable for scenarios like counters and accumulators.