I have a simple code snippet
public class ItemManager {
private Integer itemCount = 0;
public void incrementAndPrint() {
synchronized(this) {
System.out.println(++itemCount + " ");
}
}
public static void main(String[] args) {
ItemManager manager = new ItemManager();
ExecutorService executor = Executors.newFixedThreadPool(20);
for (int i = 0; i < 10; i++) {
executor.submit(manager::incrementAndPrint);
}
executor.shutdown();
}
}
that produces 1 2 3 4 5 6 7 8 9 10 as expected. I can also create another field with Object instance and lock on it
private Integer itemCount = 0;
private Object lock = new Object();
public void incrementAndPrint() {
synchronized(lock) {
System.out.println(++itemCount + " ");
}
}
and it will also produce 1 2 3 4 5 6 7 8 9 10 as expected.
However, if I try to lock on the same object that I want to increment and print
private Integer itemCount = 0;
public void incrementAndPrint() {
synchronized(itemCount) {
System.out.println(++itemCount + " ");
}
}
the operation will stay atomic but the result is out of order: 2 1 3 4 5 6 7 8 9 10.
I know that synchronized(this)
or synchronizing the whole method will fix all my problems. I just can't understand why I can lock on one field (Object lock
), but can't lock on another (Integer itemCount
)? Shouldn't everything inside the synchronized
block be locked properly, regardless of what this object is, as long as it is a single object shared between all threads?
Integer
s in Java are immutable. When you call ++itemCount
, you're in fact performing three operations: First, the Integer
is unboxed to an int
with the value of Integer
. Then, this primitive int
is incremented, and finally the incremented int
is autoboxed back to an Integer
. So in fact, you eventually have different Integer
instances. Since you synchronize on different instances, the synchronization is meaningless, and you see the out-of-order printing.