DIFFERENCE BETWEEN
ATOMICLONG
AND LONG
When I was early in my Java career, I thought Long was enough for everything. Need to count something? Use a Long. Need to increment it? Just do count++. Simple. But that illusion breaks the day you run your code in a multi-threaded environment. If you ever handled concurrency thread pools, executors, microservices, even simple background tasks, you’ve probably seen weird numbers: counts that don’t add up, missing increments, or random non-deterministic results.
That’s when I learned the difference between Long and AtomicLong.
Long — Good Old Value Holder (But Not Safe in Multi-Threading)
Long is basically a box that holds a number. It’s:
- check_circle Immutable
- check_circle Not thread-safe
- check_circle Not designed for concurrent updates
When multiple threads modify a Long value, they don’t actually “modify” it — they create new Long objects, and race conditions happen everywhere.
Example:
Imagine you have 1000 threads incrementing a Long counter:
public class LongCounterExample {
static Long counter = 0L;
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> counter++;
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(task);
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("Final count: " + counter);
}
}You expect 1000, right? You’ll never get it. You’ll get something random like 692, 815, etc.
Why? Because counter++ is not atomic. It's actually:
- check_circle Read counter
- check_circle Add 1
- check_circle Assign new value
Multiple threads overlap steps 1–3 and overwrite each other.
That’s when AtomicLong steps in.
AtomicLong — The “Thread-Safe Counter”
AtomicLong is designed for high-performance, thread-safe operations without locking.
Key benefits:
- check_circle Atomic operations (increment, decrement, get, set)
- check_circle Lock-free (CAS — Compare And Swap)
- check_circle Designed for concurrency
Using it is almost the same as Long — but safe.
Example
import java.util.concurrent.atomic.AtomicLong;
public class AtomicLongExample {
static AtomicLong counter = new AtomicLong(0);
public static void main(String[] args) throws InterruptedException {
Runnable task = counter::incrementAndGet;
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(task);
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println("Final count: " + counter.get());
}
}
This time you will always get 1000, no matter how many threads you spawn.
The magic comes from atomic operations like:
- check_circle incrementAndGet()
- check_circle getAndIncrement()
- check_circle addAndGet(n)
- check_circle compareAndSet(expected, newValue)
These methods ensure no thread steps on another thread’s update.
When Should You Use
Let me be straightforward:
- check_circle If the variable is not shared between threads → use Long.
- check_circle If multiple threads update the same value → use AtomicLong.
- check_circle If the value represents a shared counter → AtomicLong, always.
- check_circle If performance under contention matters → AtomicLong is your friend.
Long is great in single-threaded logic or immutable configurations.
AtomicLong is built for concurrency and will save you hours of debugging “why my counter is wrong”.
Final Thoughts
The day I switched from Long to AtomicLong for shared counters, half of my concurrency bugs vanished. It's one of those simple tools that every Java developer should master early.
If you work with executors, schedulers, gRPC services, microservices, or anything multi-threaded, trust me AtomicLong will pay off.