Here's a self-contained example executable application that demonstrates that volatile on its own is not enough. Four threads increment a counter 10,000 times each, so you'd expect the counter to be 40,000 at the end. It uses a primitive int variable and an AtomicInt, and tries the exercise 5 times each.
import java.util.Collections;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
class AtomicDemo {
interface Demo extends Callable<Void> {
int getCounter();
}
static class UsePrimitive implements Demo {
private volatile int counter = 0;
public Void call() throws Exception {
for (int i = 1; i <= 10000; ++i) {
++counter;
}
return null;
}
public int getCounter() {
return counter;
}
}
static class UseAtomic implements Demo {
final AtomicInteger counter = new AtomicInteger(0);
public Void call() throws Exception {
for (int i = 1; i <= 10000; ++i) {
counter.incrementAndGet();
System.out.print("");
}
return null;
}
public int getCounter() {
return counter.get();
}
}
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newFixedThreadPool(4);
for (int i = 1; i <= 5; ++i) {
Demo demo = new UsePrimitive();
exec.invokeAll(Collections.nCopies(4, demo));
System.out.println("Count to 40000 using primitive, attempt number " + i + ": " + demo.getCounter());
}
for (int i = 1; i <= 5; ++i) {
Demo demo = new UseAtomic();
exec.invokeAll(Collections.nCopies(4, demo));
System.out.println("Count to 40000 using atomic, attempt number " + i + ": " + demo.getCounter());
}
exec.shutdownNow();
}
}
Typical output:
Count to 40000 using primitive, attempt number 1: 39711
Count to 40000 using primitive, attempt number 2: 39686
Count to 40000 using primitive, attempt number 3: 39972
Count to 40000 using primitive, attempt number 4: 39840
Count to 40000 using primitive, attempt number 5: 39865
Count to 40000 using atomic, attempt number 1: 40000
Count to 40000 using atomic, attempt number 2: 40000
Count to 40000 using atomic, attempt number 3: 40000
Count to 40000 using atomic, attempt number 4: 40000
Count to 40000 using atomic, attempt number 5: 40000
You see, only with AtomicInt do you always get the expected results.