Why Incrementing a Counter Isn’t Thread‑Safe in Java: A Hands‑On Demo

This article explains atomicity, thread safety, and thread‑unsafe operations in Java using a simple multithreaded counter example that shows how the non‑atomic i++ leads to inconsistent results despite using volatile variables.

FunTester
FunTester
FunTester
Why Incrementing a Counter Isn’t Thread‑Safe in Java: A Hands‑On Demo

The article introduces three core concepts related to concurrent programming in Java:

Atomicity : an operation (or group of operations) must execute completely without interruption, otherwise it is not atomic.

Thread safety : when multiple threads access a class, proper locking or other protection ensures that only one thread manipulates shared data at a time, preventing data inconsistency.

Thread‑unsafe : lack of protection can cause dirty reads or lost updates.

To illustrate these ideas, the article provides a concrete Java example. The class TR extends a test library and contains a volatile int i field, a method ss() that sleeps briefly and then increments i, and JUnit lifecycle methods that output markers before and after the test.

public class TR extends FanLibrary {
    private volatile int i = 0;

    public void ss() {
        // sleep to increase the chance of interleaving
        sleep(100);
        i++; // not atomic: equivalent to i = i + 1
    }

    @Before
    public void be() {
        output("before");
    }

    @Test
    public void sdfa() throws InterruptedException {
        Thread first = new Thread(() -> { ss(); });
        Thread second = new Thread(() -> { ss(); });
        first.start();
        second.start();
        first.join();
        second.join();
        output(i);
    }

    @After
    public void ds() {
        output("after");
    }
}

When the test runs, the console may show:

INFO-> before
INFO-> 1
INFO-> after

The increment operation i++ consists of reading the current value, adding one, and writing the result back. Because these steps are not performed atomically, two threads can both read the initial value 0, compute 1, and each write 1, leaving the final value as 1 instead of the expected 2. This demonstrates that even with the volatile keyword, the method is not thread‑safe without additional synchronization.

The example highlights why methods that are intended to be called from multiple threads must be designed to be atomic or protected by locks, ensuring that intermediate states are never visible to other threads.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Java
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.