I agree with @Kumar's answer.
Volatile is not sufficient - it has some implications for the memory order, but does not ensure atomicity of ++.
The really difficult thing about multi-threaded programming is that problems may not show up in any reasonable amount of testing. I wrote a program to demonstrate the issue, but it has threads that do nothing but increment counters. Even so, the counts are within about 1% of the right answer. In a real program, in which the threads have other work to do, there may be a very low probability of two threads doing the ++ close enough to simultaneously to show the problem. Multi-thread correctness cannot be tested in, it has to be designed in.
This program does the same counting task using a simple static int, a volatile int, and an AtomicInteger. Only the AtomicInteger consistently gets the right answer. A typical output on a multiprocessor with 4 dual-threaded cores is:
count: 1981788 volatileCount: 1982139 atomicCount: 2000000 Expected count: 2000000
Here's the source code:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Test {
private static int COUNTS_PER_THREAD = 1000000;
private static int THREADS = 2;
private static int count = 0;
private static volatile int volatileCount = 0;
private static AtomicInteger atomicCount = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>(THREADS);
for (int i = 0; i < THREADS; i++) {
threads.add(new Thread(new Counter()));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("count: " + count + " volatileCount: " + volatileCount + " atomicCount: "
+ atomicCount + " Expected count: "
+ (THREADS * COUNTS_PER_THREAD));
}
private static class Counter implements Runnable {
@Override
public void run() {
for (int i = 0; i < COUNTS_PER_THREAD; i++) {
count++;
volatileCount++;
atomicCount.incrementAndGet();
}
}
}
}