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