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.
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-> afterThe 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.
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.
