Synchronizing access to Immutable Integer object

2019-06-22 07:34发布

问题:

Code snippet - 1

class RequestObject implements Runnable
{
    private static Integer nRequests = 0;

    @Override
    public void run()
    {       
        synchronized (nRequests)
        {
            nRequests++;
        }
    }
}

Code snippet - 2

class RequestObject implements Runnable
{
    private static Integer nRequests = 0;
    private static Object lock = new Object();

    @Override
    public void run()
    {       
        synchronized (lock)
        {
            nRequests++;
        }
    }
}

While the second code snippet is working fine without causing any race conditions, the first one isn't successful in synchronizing access to the static data member between different instances of the same class(RequestObject). Could somebody throw more light on this. I would like to understand why the first approach isn't working.

My original implementation is the first one. The later I saw in https://stackoverflow.com/a/2120409/134387.

回答1:

You are constantly creating new Integer objects, which you then synchronize on, which at least makes it extremely confusing to think about it. So you can get the following scenario:

Thread A gets hold of the current value of nRequests (lets say 0)

Thread B queues for the same value (0)

Thread A increases nRequests (to the value 1)

Thread C gets hold of the new value and synchronizes on it, increases it and lets go of the value.

Thread A lets go of the monitor on 0

Thread B synchronizes on 0 and increases it to 1, overwriting the changes of C

With the second approach you have a single object that everybody has to synchronize on. Which is exactly what you want.



回答2:

Instances of Integer are immutable, nRequests++ therefore creates a new Integer object to hold the result, and stores it in nRequests. The synchronized statement synchronizes on an object. Therefore, the threads will synchronize on different objects. Yes, there may be only one thread in a synchronized block on the same object at the same time, but different threads may be in synchronized blocks of different objects at the same time ...

The easiest way to synchronize access to static state is putting it in a static synchronized method:

static synchronized void increment() {
    nRequests++;
}

This is equivalent to the following synchronized block:

synchronized (RequestObject.class) {
    nRequests++;
}

where RequestObject is the class containing the static field.



回答3:

The problem is that the Integer class is immutable in java. So every thread synchronize on a different object because nRequests++ creates a new object on each invocation.

In the second case the lock object is the same for every instance and succesfully serialize the acces of threads to the nRequests variable.