Syncronization block not working as expected, when aquired lock on required object, but works fine when applied on ‘this’

public class SyncBlockIssue {
        
        public static void main(String[] args) {    
            Thread r1= new Multi();
            Thread t1= new Thread(r1);          
            Thread t2= new Thread(r1);
            t1.start();
            t2.start();
        }
    }

    class Multi extends Thread
    {
        Integer count =new Integer(0);      
        @Override
        public void run() {
            for(int i=0;i<100000;i++)
            {               
                    synchronized (count) {      
                    //synchronized (this) {   //working fine with this
                    count++;
                }               
            }
            System.out.println(Thread.currentThread().getName() + "  "+count);          
        }       
    }

Above code is printing 2 million from 2nd thread, no matter how many times i run when acquire lock on ‘this’, but where as printing arbitrarily count, when acquired lock ‘count’ object. could someone explain the difference.

Answer

Here’s my guess, since I can’t use comments to type so many words.

Integer objects are immutable:

    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

So, when you do count++, a new Integer object will be created. The count reference is then directed to the new object.

Assume the following flow:

  1. Thread r1 acquires count lock (Notice that the count object is locked), at this point thread r2 is blocked.

  2. r1 invokes count++, a new Integer object is created. The count reference is then directed to the new object. And r1 release lock.(This lock refers to the previous Integer object).

  3. r2 which had been blocked acquires lock.

  4. r1 also acquires the lock because count has already pointed to a new object.

  5. Both threads execute count++: problem arises.

The following code proves that both threads are holding the “lock” at the same time

for(int i=0; i<2; i++)
        {
            synchronized (count) {
                System.out.println(Thread.currentThread().getName() + "get lock");
                count++;
                if (i == 1) {
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "release lock");
            }
        }