Leaving aside the non-atomicity of the operations x++
and x--
, the effects of which are noted in the comments to the OP, there is another effect which does not rely on non-atomicity of individual operations.
The consistency of the sign (all output positive) is due to the fact that, when one thread finishes its loop and prints the current value of x, there are still many other threads which have so far incremented x but have not yet decremented it. (I mean the other threads are between the increment and decrement statements, so they've incremented x N times but decremented it (N-1) times.)
This can be proved with the following modification to your code:
private static class MyThreadCode implements Runnable {
private static AtomicInteger x = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
x.incrementAndGet();
x.decrementAndGet();
}
System.out.println(x + " " + Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(x + " " + Thread.currentThread().getName());
}
}
You will see that, before the 10-second sleep, all threads print a positive number as they complete the loop (but other threads are still running). After the 10-second sleep, all threads then print the final result, which is always 0, because after 10 seconds, all threads have completed their loops and done as many decrements as increments.
To avoid this problem, you should use synchronization to ensure that the increment and decrement operations are done atomically. The following modification results in all output being 0:
synchronized (x) {
x.incrementAndGet();
x.decrementAndGet();
}
(Though if you're using x
always inside a synchronized block, then you might as well make it a primitive int.)