Search⌘ K
AI Features

AtomicLong

Explore AtomicLong in Java concurrency to understand how it provides atomic operations for long values using compare-and-swap instructions. Learn its performance benefits versus traditional locking, how it differs from the long type, and methods to simulate atomic double values for thread-safe programming.

If you are interviewing, consider buying our number#1 course for Java Multithreading Interviews.

Overview

AtomicLong is the equivalent class for long type in the java.util.concurrent.atomic package as is AtomicInteger for int type. The AtomicLong class represents a long value that can be updated atomically, i.e. the read-modify-write operation can be executed atomically upon an instance of AtomicLong. The class extends Number.

Like the AtomicInteger class the AtomicLong` makes for great counters, sequence numbers etc as it uses the compare-and-swap (CAS) instruction under the hood. The CAS instruction doesn’t penalize competing threads for access to shared data/state with suspension as locks do. In general, suspension and resumption of threads involves significant overhead and under low to moderate contention non-blocking algorithms that use CAS outperform lock-based alternatives.

Performance

To demonstrate the performance of AtomicLong we can construct a crude test, where a counter is incremented a million times by ten threads to reach a total of ten million. We’ll time the run for an AtomicLong counter and an ordinary long counter. The widget below outputs the results:

Java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;
class Demonstration {
static long simpleCounter;
static AtomicLong atomicCounter;
public static void main( String args[] ) throws Exception {
test(true);
test(false);
}
synchronized static void incrementSimpleCounter() {
simpleCounter++;
}
static void test(boolean isAtomic) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
try {
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
if (isAtomic) {
atomicCounter.incrementAndGet();
} else {
incrementSimpleCounter();
}
}
}
});
}
} finally {
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.HOURS);
}
long timeTaken = System.currentTimeMillis() - start;
System.out.println("Time taken by " + (isAtomic ? "atomic long counter " : "long counter ") + timeTaken + " milliseconds.");
}
}

Difference with long

Remember that AtomicLong isn’t a drop-in replacement for long. Specifically, just like the AtomicInteger class, the AtomicLong doesn’t override equals() or hashcode() and each instance is distinct. The widget below demonstrates that the same long value for two different AtomicLong instances doesn’t hash to the same bucket.

Java
import java.util.concurrent.atomic.*;
import java.util.HashMap;
class Demonstration {
public static void main( String args[] ) {
// create map
HashMap<AtomicLong, String> mapAtomic = new HashMap<>();
HashMap<Long, String> mapLong = new HashMap<>();
// create two instances with the same long value 5
AtomicLong fiveAtomic = new AtomicLong(5);
AtomicLong fiveAtomicToo = new AtomicLong(5);
// create two long instances
Long fiveLong = new Long(5);
Long fiveLongToo = new Long(5);
// Though the key is 5, but the two AtomicInteger instances
// have different hashcodes
mapAtomic.put(fiveAtomic, "first five atomic");
mapAtomic.put(fiveAtomicToo, "second five atomic");
System.out.println("value for key 5 : " + mapAtomic.get(fiveAtomic));
// With Integer type key, the second put overwrites the
// key with Integer value 5.
mapLong.put(fiveLong, "first five long");
mapLong.put(fiveLongToo, "second five long");
System.out.println("value for key 5 : " + mapLong.get(fiveLong));
}
}

Using AtomicLong to simulate atomic double

Previously, we demonstrated how the AtomicInteger class can be used to create atomic byte and atomic float types. Similarly, an equivalent atomic class for double primitive type doesn’t exist, however, we can simulate one using AtomicLong as we did for the primitive type byte. The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion.This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead.

To create our custom AtomicDobule type, we extend from the class Number and override several of its functions. Under the hood, we save the floating point’s bit representation in an instance of AtomicLong. The class listing appears below with comments for explanation:

Java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
class Demonstration {
public static void main( String args[] ) throws Exception {
AtomicDouble atomicDouble = new AtomicDouble(0.23d);
ExecutorService executorService = Executors.newFixedThreadPool(100);
// create 50 threads that each adds 0.17 a 1000 times. At the end our atomic
// double should sum up to 8500.23 but you'll see a close enough value since
// double isn't precise. However, multiple runs of the program should produce
// the same value indicating the class AtomicDouble the thread-safe.
try {
for (int i = 0; i < 50; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++)
atomicDouble.getAndAdd(0.17d);
}
});
}
} finally {
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.HOURS);
}
// print after the shift operations are complete
System.out.println(atomicDouble.doubleValue());
}
}

The AtomicDouble class in this lesson is shown for instructional purposes only. If you need to use this primitive type atomically, you can find well-written and well-tested libraries on the internet. For instance, Google’s Guava library offers an AtomicDouble type.