4

While working on a multi-threading program and I observed very strange behavior.

When Integer object is used as a lock then it seems that multiple threads can be in synchronized block. Even thought that is not expected.

If I use any other static member like 's', 'o' and 'c' define in below program, it works as expected.

Code-

public class MyThread extends Thread{
    private static Integer ii=1;                        //Works fine
    private static Integer i=1;
    private static String s="1";                        //Works fine
    private static Object o= new Object();              //Works fine
    private static Class<MyThread> c= MyThread.class;   //Works fine

    public void run(){
        synchronized(i){
            System.out.print(i++ +" ");
            System.out.print(i+"   ");
        }
    }
    public static void main(String[] str) throws InterruptedException{
        for(int i=0;i<100;i++){
            MyThread t= new MyThread();
            t.start();
        }
        Thread.sleep(100);
        System.out.println();
        MyThread t= new MyThread();
        t.start();t.join();
        if(i!=102)
            System.out.println("fail");
    }
}

output-

2 3   3 4   5 6   8 9   9 10   10 11 12   12 13   13 14   14 15   16 17   1 17   15 17   12   17 18   18 20   20 21   21 22   7 22 6 23   4 23   23 24   24 25   25 26   23   22   19 27   26 27   27 28   28 29   29 30   30 31   31 32   32 33   33 34   34 35   35 36   36 37   37 38   38 39   39 41   40 41 42   42   42 43   43 44 45   45   45 48   47 48   46 48   48 49   49 50   50 51   51 52   52 53   53 54   54 55   55 56   56 57   57 58   58 59   59 60   60 61   61 62   62 63 64   64 65   64   65 66   66 67   68 69   69 70   67 70   70 71   71 72   72 73   73 75   74 76   75 76   76 77   77 79 80   78 80   80   80 83 84   82 85   85 86   86 87   87 88   88 89   89 90   81 94   93 94   92 94   91 94   90 94   84   94 96   96 98   98 99   84 99   97 99   95 99   99 100   100 101   
101 102  

As you can see when it printed "10 11 12" there were two threads executing in synchronized block.

Is it I am doing something wrong or am I missing something?

Does it have to do with some optimization behind the scene? Because if I used 'ii' for locking everything works perfectly.

Also when 'i' is used it does print 'fail' but RARELY.

Below is java version used to run the program. java -version java version "1.7.0_51" Java(TM) SE Runtime Environment (build 1.7.0_51-b13) Java HotSpot(TM) Client VM (build 24.51-b03, mixed mode, sharing)

You can use below program to run the program to run this bunch of times and see the results.

public class MyThread extends Thread{
    private static Integer ii=1;
    private static Integer i=1;
    private static String s="1";
    private static Object o= new Object();
    private static Class<MyThread> c= MyThread.class;

    public void run(){
        synchronized(ii){
            System.out.print(i++ +" ");
            System.out.print(i+"   ");
        }
    }
    public static void main(String[] str) throws InterruptedException{
        for(int j=0;j<100;j++){
            for(int i=0;i<100;i++){
                MyThread t= new MyThread();
                t.start();
            }
            Thread.sleep(50);
            System.out.println();
            MyThread t= new MyThread();
            t.start();t.join();
            if(i!=102)
                System.out.println("fail");
            Thread.sleep(50);
            i=1;
            System.out.println();
        }
    }
}
  • Synchronizing on Integer seems a bad idea : http://stackoverflow.com/questions/659915/synchronizing-on-an-integer-value – StuartLC Feb 26 '14 at 16:31
  • One of the reasons I don't like auto-boxing. Though synchronizing on `Integer` is a dangerous thing anyway due to possible unexpected effects because of the instance caching done by class `Integer`. – Dirk Feb 26 '14 at 16:33

4 Answers4

7

This

i++

is equivalent to

i = Integer.valueOf(i.intValue() + 1)

In other words, i is now referencing a different object, different than the one you are originally synchronizing on.

If a thread happens to get to the synchronized block after the i has changed, it will also get in because it acquires the monitor on a different object.

Because if I used 'ii' for locking everything works perfectly.

You aren't changing the reference to ii anywhere.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • So does it mean if we lock on `String` object and do some processing on `String` object inside `synchronized` block, so that it will point to some other String object we will get the same kind of behavior? – sakura Feb 26 '14 at 16:35
  • @sakura `synchronized` works by obtaining the monitor on the object referenced by the variable (or expression). If you change what that variable has as a reference and a different thread reaches a synchronized block on the variable, then it acquires the lock a different object. In that sense, yes. – Sotirios Delimanolis Feb 26 '14 at 16:36
1

This is exactly why locks should be declared final. And this is an example of how you can shoot yourself in the foot.

Giovanni Botta
  • 9,626
  • 5
  • 51
  • 94
0

I think the problem is that you are changing the value of 'i' by doing i++ inside the synchronized block, which causes the object assigned to i to be different each time (check this in Eclipse, look at the object id of the variable before and after the increment operation).

codebox
  • 19,927
  • 9
  • 63
  • 81
0

Integer is an immutable class. When you perform an operation like ++ on it, you're creating a new instance. That can cause the threads to synchronize on different objects, enabling them to access the synchronized block at the same time.

Zach Thacker
  • 2,529
  • 1
  • 23
  • 28