Java Atomic Variables Tutorial 🎯

beginner
10 min

Java Atomic Variables Tutorial 🎯

Welcome to our deep dive into Java Atomic Variables! This tutorial is designed to help both beginners and intermediates understand the concept from scratch and delve deeper into its practical applications. 📝

What are Atomic Variables? 💡

Atomic variables, in the context of Java, are a way to ensure that complex operations on variables are executed in a thread-safe manner. They are part of the java.util.concurrent.atomic package. These variables provide higher-performance alternatives to the synchronization offered by the synchronized keyword.

Why do we need Atomic Variables? 📝

When multiple threads access and modify shared variables, they can lead to concurrency issues like race conditions and inconsistent states. Atomic variables help mitigate these issues by ensuring that operations on these variables are atomic, meaning they are executed as a single, indivisible operation.

Working with Atomic Variables 💡

Java provides several types of atomic variables, including:

  1. AtomicInteger for integer values
  2. AtomicLong for long values
  3. AtomicReference for objects
  4. and more!

Let's look at an example using AtomicInteger.

Example 1: AtomicInteger 📝

java
import java.util.concurrent.atomic.AtomicInteger; public class AtomicExample { public static AtomicInteger counter = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(AtomicExample::increment).start(); } } public static void increment() { counter.incrementAndGet(); // This operation is atomic! System.out.println("Thread " + Thread.currentThread().getId() + " incremented the counter to: " + counter.get()); } }

In this example, we create an AtomicInteger variable counter. The incrementAndGet() method both increments the counter and returns the new value as an integer. When run, this program will output the counter being incremented by each thread, demonstrating the atomicity of the operation.

Quiz 💡

Quick Quiz
Question 1 of 1

What does the `incrementAndGet()` method do in the provided example?

Advanced Example 💡

In this example, we demonstrate the use of an AtomicReference to maintain a stack in a thread-safe manner.

java
import java.util.concurrent.atomic.AtomicReference; import java.util.LinkedList; public class StackExample { private static AtomicReference<Node> top = new AtomicReference<>(new Node(null)); static class Node { int data; Node next; public Node(int data) { this.data = data; } } public static void push(int data) { Node newNode = new Node(data); while (true) { Node currentTop = top.get(); Node newTop = new Node(); newTop.next = currentTop; newTop.data = data; if (top.compareAndSet(currentTop, newTop)) break; } } public static int pop() { Node currentTop; while ((currentTop = top.get()) != null) { Node nextNode = currentTop.next; if (nextNode == null) { throw new RuntimeException("Stack is empty"); } if (top.compareAndSet(currentTop, nextNode)) { return currentTop.data; } } throw new RuntimeException("Error while popping from stack"); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 5; j++) { StackExample.push(j * 10); } }).start(); } new Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println("Popped: " + StackExample.pop()); } }).start(); } }

In this example, we create a thread-safe stack using AtomicReference. The push() method adds elements to the stack, while the pop() method removes and returns the top element.

And there you have it! You've learned about Java Atomic Variables, how they help with concurrency issues, and how to use them in practice. Happy coding! 💡