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?
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:
And here's the second thread.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With