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.
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-> afterThe 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
