Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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");
        c.start();c1.start();
   }
}
class Check extends Thread{
    Check(String name){super(name);}
    private Integer ab = 2;
    public void run(){
        synchronized (ab) {
            System.out.println(Thread.currentThread().getName());
            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?

like image 304
Santanu Avatar asked Jun 30 '16 08:06

Santanu


2 Answers

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;

to

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.

like image 195
Mshnik Avatar answered Oct 31 '22 23:10

Mshnik


This is happening because of Ingeter Pool concept in java.

Integers ranging between -128 and 127 (inclusive), are used in same way as String pool.

So, when you use private Integer ab = 2;, ab is shared for both objects of Check.

You can use value > 128 or any other type of object so that your code won't be synchronized.

You may look at answers here: Why does the behavior of the Integer constant pool change at 127? for understanding Integer Pool concept.

like image 4
codingenious Avatar answered Oct 31 '22 23:10

codingenious