简体   繁体   English

当Integer对象用作同步块中的锁时,可能出现并发问题

[英]Potential concurrency issue when Integer object is used as a lock in synchronized block

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. 当Integer对象用作锁时,似乎多个线程可以处于同步块中。 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. 如果我在下面的程序中使用任何其他静态成员,如's','o'和'c',则按预期工作。

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. 正如您在打印“10 11 12”时所看到的那样,在同步块中有两个线程正在执行。

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. 因为如果我使用'ii'锁定一切都很完美。

Also when 'i' is used it does print 'fail' but RARELY. 此外,当使用'i'时,它确实打印'失败'但是RARELY。

Below is java version used to run the program. 下面是用于运行程序的java版本。 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) java -version java version“1.7.0_51”Java(TM)SE运行时环境(版本1.7.0_51-b13)Java HotSpot(TM)客户端VM(版本24.51-b03,混合模式,共享)

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();
        }
    }
}

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. 换句话说, i现在引用一个不同的对象,与您最初同步的对象不同。

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. 如果一个线程在i发生变化后碰巧到达synchronized块,它也会进入,因为它在另一个对象上获取了监视器。

Because if I used 'ii' for locking everything works perfectly. 因为如果我使用'ii'锁定一切都很完美。

You aren't changing the reference to ii anywhere. 您没有在任何地方更改对ii的引用。

This is exactly why locks should be declared final . 这正是为什么锁应该被声明为final And this is an example of how you can shoot yourself in the foot. 这是一个如何用脚射击自己的例子。

我认为问题在于你通过在synchronized块中执行i++来改变'i'的值,这导致每次分配给i的对象都不同(在Eclipse中检查它,查看变量的对象id之前并且在增量操作之后)。

Integer is an immutable class. Integer是一个不可变的类。 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. 这可能导致线程在不同对象上同步,使它们能够同时访问synchronized块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM