Search code examples
javamultithreadingconcurrencysynchronized

Why synchronizing on the field variable and incrementing it inside synchronized block results in print out of order?


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?


Solution

  • Integers 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.