Preparing for Java and Spring Boot Interview?

Join my Newsletter, its FREE

Wednesday, September 27, 2023

What are AtomicInteger and AtomicLong in Java? How it works? compareAndSet() Example Tutorial

Concurrency is a fundamental aspect of modern software development, and Java provides a variety of tools and classes to help developers manage shared resources and coordinate the behavior of multiple threads. Among these tools one is atomic variables, which ensure that certain operations on shared data are performed atomically, I mean in one shot, without the need for explicit synchronization. In the past I talked about difference between atomic, synchronized, and volatile in Java and In this article, we'll explore atomic variables in Java, with a focus on AtomicInteger and AtomicLong, and explain how they work and how to use them effectively in Java. 

What are Atomic Variables?

An atomic variable is a variable that can be read and modified atomically, meaning that the read and write operations on the variable are indivisible. In a multithreaded environment, this ensures that multiple threads can safely access and modify the variable without causing data corruption or race conditions. 

Java provides a set of classes in the java.util.concurrent.atomic package to work with atomic variables. JDK or Java Development kit  provides AtomicBoolean, AtomicInteger, AtomicLong, and AtomicReference as atomic variables in Java. 

What is AtomicInteger and AtomicLong in Java? How it works? compareAndSet() Example Tutorial

What are AtomicInteger and AtomicLong in Java?

Java provides two commonly used atomic variable classes: AtomicInteger and AtomicLong. These classes are designed to work with integer and long values, respectively, and provide atomic operations for common operations like incrementing, decrementing, adding, and setting values.

1. AtomicInteger

AtomicInteger is a class that allows you to work with atomic integer values. It provides methods for atomic operations such as incrementAndGet(), decrementAndGet(), addAndGet(int delta), and compareAndSet(int expect, int update). 

These methods ensure that the operations are performed atomically, making it suitable for various concurrency scenarios where multiple threads need to work with integer values.

2. AtomicLong

AtomicLong is similar to AtomicInteger but designed for long integer values. It provides atomic operations like incrementAndGet(), decrementAndGet(), addAndGet(long delta), and compareAndSet(long expect, long update) for working with long values atomically.

How Atomic Variables Works in Java?

Atomic variables achieve thread safety and atomicity through hardware and JVM-level mechanisms like Compare-And-Swap (CAS) or Load-Link/Store-Conditional (LL/SC) instructions, depending on the platform. Here's a simplified explanation of how CAS works:

Reading the Value: When a thread reads the value of an atomic variable, it obtains the current value.

Modifying the Value Atomically: When a thread wants to update the value, it provides both the expected (current) value and the new value it wants to set. The atomic variable's internal logic then checks if the current value matches the expected value.

Conditional Update: If the current value matches the expected value, the atomic variable atomically updates the value with the new value provided by the thread. If the current value doesn't match the expected value, the operation fails, and the thread can retry the operation or take another action as needed.

The key point is that the entire read-modify-write operation is done atomically, without other threads being able to interfere. This ensures that operations on atomic variables are thread-safe and that no two threads can inadvertently update the variable simultaneously.

AtomicInteger and AtomicLong Example in Java

Here's a simple example of using AtomicInteger and AtomicLong:

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class AtomicVariableExample {
    public static void main(String[] args) {
        AtomicInteger atomicInt = new AtomicInteger(0);
        AtomicLong atomicLong = new AtomicLong(0);

        // Increment and decrement operations
        atomicInt.incrementAndGet(); // Increment atomicInt by 1
        atomicInt.decrementAndGet(); // Decrement atomicInt by 1

        // Add and subtract operations
        atomicLong.addAndGet(10); // Add 10 to atomicLong
        atomicLong.addAndGet(-5); // Subtract 5 from atomicLong

        // Compare and set operation
        boolean updated = atomicInt.compareAndSet(0, 100); 
        // Set to 100 if the current value is 0

        System.out.println("AtomicInt updated: " + updated); // Will print "true"

In this example, we create instances of AtomicInteger and AtomicLong, perform various atomic operations on them, and demonstrate the use of compareAndSet() to conditionally update the value.

Best Practices while using Atomic variables in Java

Here are a few best practices you should follow while using atomic variables in Java:

1. Choose the Appropriate Type
You should select AtomicInteger or AtomicLong based on the type of value you need to work with.

2. Use Atomic Operations Whenever Possible
Instead of manually synchronizing code blocks, prefer atomic operations when updating shared variables. Atomic operations are often more efficient and less error-prone.

3. Understand Limitations
While atomic variables provide thread safety for individual operations, you may still need additional synchronization for complex operations involving multiple atomic variables. In that case you use synchronized keyword for mutual exclusion. 

4. Performance Considerations
 Benchmark your application to ensure that atomic variables meet your performance requirements. In some cases, other synchronization techniques might be more appropriate. AtomicLong and AtomicInteger doesn't perform well when exposed to high number of threads and during high contention, consider using LongAdder in those cases. 

5. Keep Operations Simple
Avoid complex logic within atomic operations, as it can make it harder to reason about thread safety.

That's all about what is atomic variables in Java and how to use them. Atomic variables, such as AtomicInteger and AtomicLong, provide a valuable tool for managing shared data in multithreaded Java applications. 

They ensure that common operations are performed atomically, eliminating the need for explicit synchronization and reducing the risk of race conditions. 

When used appropriately, atomic variables can help you write efficient and thread-safe concurrent code. Understanding how they work and when to use them is essential for building robust and scalable concurrent applications in Java.

Other Java Concurrency Articles you may like
  • 5 Courses to Learn Java Multithreading in-depth (courses)
  • 10 Java Multithreading and Concurrency Best Practices (article)
  • How does Exchanger works in Java Multithreading (tutorial)
  • 10 Courses to learn Java in for Beginners (courses)
  • Difference between CyclicBarrier and CountDownLatch in Java? (answer)
  • How to avoid deadlock in Java? (answer)
  • Understanding the flow of data and code in Java program (answer)
  • The Complete Java Developer RoadMap (roadmap)
  • Is Java Concurrency in Practice still valid (answer)
  • How to do inter-thread communication in Java using wait-notify? (answer)
  • 10 Tips to become a better Java Developer (tips)
  • 5 Essential Skills to Crack Java Interviews (skills)
  • How does CompletableFuture works in Java? (completableFuture)
  • Top 5 Books to Master Concurrency in Java (books)
  • Top 50 Multithreading and Concurrency Questions in Java (questions)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions or feedback then please drop a note.

1 comment :

Anonymous said...

I have never used AtomicInteger, AtomicLong, LongAdder or LongAccumulator, I don't think they are needed for common Java app developer. May be API and library designer use that but not common Java developer. Only thin I use with related to threading is Executor, ForkJoinPool, Runnable, Callable etc.

Post a Comment