The problem is that by default, the JVM is allowed to perform a lot of tricks when working with multiple threads. It may e.g. reorder statements, duplicate variables across threads and other things.
What can go wrong in your MutableInteger
:
- Thread 1 calls set(5)
- Thread 2 calls set(3)
- Thread 1 calls get() and receives 5 (not 3) as the result, even though Thread 2 has finished calling set(3) on it.
How is SynchronizedInteger
better? Entering a synchronized
method (or block) forces the JVM to do two things:
- only one thread may enter at a time, all others have to wait (exclusive lock) AND
- all changes to the synchronization target are made visible to all threads immediately when the block is exited
So in the example above, Thread 1 will receive 3 when calling get()
, not 5 as before.
By the way, for primitives (int, char, float...) you can use the volatile
keyword to force changes to be visible to all threads immediately. Alternatively, use the builtin AtomicInteger
and friends for better performance than synchronized
methods.