Fundamentals 4 min read

Why a Simple Increment Isn’t Thread‑Safe: A Java Concurrency Demo

This article explains atomicity, thread safety, and race conditions in Java by showing how a volatile counter increment can produce nondeterministic results when accessed concurrently, complete with sample code and console output analysis.

FunTester
FunTester
FunTester
Why a Simple Increment Isn’t Thread‑Safe: A Java Concurrency 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, or not at all.

Thread safety : multiple threads can safely access shared data when proper locking or synchronization is applied, preventing inconsistent or polluted data.

Thread‑unsafe : lack of protection can lead to dirty reads and race conditions.

To illustrate why a thread‑safe method must be atomic, the author provides a Java example where two threads invoke a method that increments a shared volatile integer i. The ss() method deliberately sleeps for 100 ms to increase the chance of interleaving, then performs i++, which is not an atomic operation.

public class TR extends FanLibrary {
    private volatile int i = 0;
    public void ss() {
        sleep(100); // increase likelihood of race condition
        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 produces console output similar to:

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

The result shows that the final value of i is often 1 instead of the expected 2. This occurs because i++ expands to a read‑modify‑write sequence ( i = i + 1) that is not atomic. When both threads read the initial value 0 before either writes back, each computes i + 1 = 1 and then writes 1, overwriting the other’s update. Consequently, the counter ends up with the same value as if only one increment had occurred.

The example demonstrates that without external synchronization, methods that modify shared state are not thread‑safe, even if the field is declared volatile. Proper synchronization (e.g., using synchronized, AtomicInteger, or other locking mechanisms) is required to guarantee atomicity and avoid race conditions.

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.

concurrencymultithreadingatomicityexamplethread-safety
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.