Concurrency Made Simple: The Role of Atomic Variables

Namrata
3 min readJul 25, 2024

--

Atomic variables in Java are part of the java.util.concurrent.atomic package and provide a way to perform atomic operations on single variables. These operations are thread-safe and ensure that the variable's state is updated consistently without the need for explicit synchronization.

Photo by Pawel Czerwinski on Unsplash

Key Characteristics of Atomic Variables

  1. Atomicity: Operations on atomic variables are indivisible. This means that once an operation starts, it completes without any interference from other threads.
  2. Lock-Free: Atomic variables use low-level atomic machine instructions (like compare-and-swap) to ensure thread safety without the overhead of locks.
  3. Non-Blocking: Since they don’t use locks, atomic variables avoid issues like deadlocks and contention that can occur with traditional locking mechanisms.

How Atomic Variables Work

Atomic variables work by using low-level atomic operations provided by the hardware, such as compare-and-swap (CAS).

Compare-and-swap (CAS) is a low-level atomic operation used in concurrent programming to achieve synchronization without using locks. It is a hardware-supported instruction that operates on a memory location and performs the following steps atomically:

  1. Read the current value of the memory location.
  2. Compare the current value with an expected value.
  3. Update the memory location to a new value if and only if the current value matches the expected value.

The key point is that these steps are performed as a single, indivisible operation, ensuring that no other thread can interfere between the comparison and the update.

Why CAS is Useful

CAS is useful because it allows multiple threads to attempt to update a shared variable concurrently without using locks. If a thread fails to update the variable (because another thread has already changed it), it can retry the operation. This lock-free approach can lead to better performance and scalability in highly concurrent applications.

Example: Using AtomicInteger with CAS

Let’s look at how AtomicInteger uses CAS internally to provide atomic operations:java

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
int oldValue, newValue;
do {
oldValue = count.get(); // Read the current value
newValue = oldValue + 1; // Calculate the new value
} while (!count.compareAndSet(oldValue, newValue)); // Attempt to update using CAS
}
public int getCount() {
return count.get(); // Atomically retrieves the current value
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter counter = new AtomicCounter();
// Create multiple threads to increment the counter
Thread t1 = new Thread(counter::increment);
Thread t2 = new Thread(counter::increment);
Thread t3 = new Thread(counter::increment);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
// Get the final count
System.out.println("Final count: " + counter.getCount()); // Should print 3
}
}

Explanation

  1. Read the Current Value: The oldValue is read from the AtomicInteger using the get() method.
  2. Calculate the New Value: The newValue is calculated by incrementing the oldValue.
  3. CAS Loop: The compareAndSet() method attempts to update the AtomicInteger from oldValue to newValue. If the current value of the AtomicInteger has not changed since it was last read, the update is successful, and the loop exits. If the value has changed (due to another thread's update), the loop retries with the new current value.

Hardware Support for CAS

Modern CPUs provide hardware support for CAS operations, often through specific instructions like CMPXCHG on x86 architectures. These instructions are designed to perform the read-modify-write sequence atomically, ensuring that no other thread can intervene during the operation.

Advantages of CAS

  1. Lock-Free: CAS allows for lock-free algorithms, reducing the risk of deadlocks and contention.
  2. Performance: CAS can be more efficient than traditional locking mechanisms, especially in scenarios with high contention.
  3. Scalability: CAS-based algorithms can scale better in multi-core systems because they avoid the overhead associated with locks.

If you enjoyed this, please give it some claps to help it reach more people. For more stories like this, follow me.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Namrata
Namrata

Written by Namrata

Engineering @Microsoft A software developer writing her daily bits . https://www.linkedin.com/in/namrataagarwal5/

No responses yet

Write a response