Santanu Santanu - 8 months ago 35
Java Question

Thread Synchronization on Integer instance variable

public class Main{
public static void main(String[] args) throws Exception {
// Creating objects for class Check(2 different objects)
Check c = new Check("s1");
Check c1 = new Check("s2");
class Check extends Thread{
Check(String name){super(name);}
private Integer ab = 2;
public void run(){
synchronized (ab) {
for(int i=0;i<10;i++)System.out.print(i+" ");

Here i have synchronized on the variable ab. And i have created two different instances of class Check also, but i am always getting output for s1 followed by s2 or vice versa, but not mixed, why is that so? when i have already created two separate objects(in main), so two different threads, two different ab variables, so how it becomes a shared resource for the two different objects?


TL;DR - it's because of Integer pooling. Make ab an Object (i.e. Object ab = new Object()) to guarantee that each instance of Check's locking doesn't interfere with the others.

I was initially puzzled as well. Interestingly enough, if you change

private Integer ab = 2;


private Object ab = new Object();

the synchronization goes away (you get different outputs on every run). Back with ab as an Integer, I ran your code in debug mode (with a breakpoint on the print thread name line) and found the following. Here's the first thread:

First thread variables

And here's the second thread.

Second thread variables

Notice that it's actually the same object, Integer@443. Even though you thought you were getting two different objects, both ab fields in the two Check instances refer to the same object in memory! Therefore yes, there is correct synchronization. The question, then, is how saying Integer ab = 2 twice gets you the same Integer object in memory.

By saying Integer ab = 2, you are using autoboxing, by which a primitive value (of type int) is automatically converted into the corresponding Object type Integer. This is equivalent to calling the autoboxing method call:

private Integer ab = Integer.valueOf(2);

If we look into Integer.valueOf, we notice that it has a pool for values within a certain range:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);

For most conventional settings, this will include the value 2. Thus you are getting the same Integer value in memory when you call this method.