Fundamentals 4 min read

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

This article explains atomicity, thread safety, and thread‑unsafe behavior in Java, demonstrates a simple class with a volatile counter, runs two concurrent threads that increment it, shows the non‑atomic nature of i++, and analyzes why the final value may remain 1.

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

The article introduces three core concepts for concurrent programming in Java:

Atomicity : an operation (or group of operations) must either complete entirely without interruption or not execute at all.

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

Thread‑unsafe : lack of protection can lead to “dirty” data where concurrent modifications overwrite each other.

To illustrate why a thread‑safe method must be atomic, the article presents a concrete example. The class TR extends FanLibrary and contains a volatile int i counter. The method ss() sleeps briefly (to increase the chance of a race) and then performs i++. Two test methods are annotated with JUnit’s @Before, @Test, and @After:

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

    public void ss() {
        sleep(100); // to make the race more likely
        i++;
    }

    @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");
    }
}

Running the test creates two threads that invoke ss() concurrently. The console may display the following sequence (order of the first and last lines can vary):

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

The key observation is that i++ is not an atomic operation; it consists of reading i, adding one, and writing the result back. If both threads read the initial value 0 before either writes, each computes 1 and writes it, so the final value remains 1 instead of 2. This race condition demonstrates why the method is thread‑unsafe.

In summary, without external synchronization, a class that allows concurrent method calls can exhibit nondeterministic behavior. Ensuring atomicity—either by using synchronized blocks, atomic classes, or other concurrency utilities—is essential for true thread safety.

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.

Javaconcurrencythread safetymultithreadingatomicity
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.