11
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?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Santanu
  • 893
  • 3
  • 10
  • 24
  • 5
    B.T.W., you are not synchronizing on the _variable_, you are synchronizing on the object to which the variable refers. It's an important distinction because some people have made the mistake of not realiznig that variable can refer to different objects at different times, and some have made the mistake of not realizing that the same object can be referenced by more than one variable. – Solomon Slow Jun 30 '16 at 12:44
  • This code looks more like a test-code than a real case, but if you want to synchronize on a lock-free, but thread-safe, [Number](https://docs.oracle.com/javase/8/docs/api/java/lang/Number.html), you can use an [AtomicInteger](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html). – Jean-François Savard Jun 30 '16 at 13:47
  • @jameslarge: I understand, what you are saying. I was not precise onto that because my problem focused on some other point. Anyways, thanks. :) – Santanu Jun 30 '16 at 14:22
  • @Jean-FrançoisSavard: Yes, its a test-code, no relevance to real projects. Just checking out. Yes, AtomicInteger, i know that. Thank you. :) – Santanu Jun 30 '16 at 14:22
  • @Jean-François: to clarify, atomicinteger is more an alternative to locking, it's not something you should use as a lock. – Nathan Hughes Jun 30 '16 at 15:16
  • @NathanHughes True. – Jean-François Savard Jun 30 '16 at 15:17
  • While in this specific case, both threads are synchronizing on the same `Integer` instance, it should be emphasized, that there is nothing wrong with two threads apparently running one after another. If two threads do not synchronize, they have no timing relationship at all, not even a parallel running guaranty. So one appearing to run entirely after another, is an entirely plausible outcome *especially* for short running threads like the two of the question. – Holger Jun 30 '16 at 17:06

3 Answers3

12

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.

Mshnik
  • 7,032
  • 1
  • 25
  • 38
4

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.

Community
  • 1
  • 1
codingenious
  • 8,385
  • 12
  • 60
  • 90
  • 2
    If you need explicitly distinct objects you should explicitly create them. Using larger integers will give you distinct objects in current implementations but is not gauranteed to do so. – plugwash Jun 30 '16 at 16:22
1

If you take a look at bytecode you would probably see this code:

LINENUMBER 15 L1
ALOAD 0
ICONST_2
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
PUTFIELD com/example/Check.ab : Ljava/lang/Integer;

Java tries to box the primitive value 2 to object by calling #valueOf of Integer class. And as we know, Integer's valueOf implements Flyweight pattern. Thus, the same object is shared between instances.

hahn
  • 3,588
  • 20
  • 31