簡體   English   中英

當Integer對象用作同步塊中的鎖時,可能出現並發問題

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

在處理多線程程序時,我觀察到了非常奇怪的行為。

當Integer對象用作鎖時,似乎多個線程可以處於同步塊中。 甚至認為這是不可預期的。

如果我在下面的程序中使用任何其他靜態成員,如's','o'和'c',則按預期工作。

碼-

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

輸出 -

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  

正如您在打印“10 11 12”時所看到的那樣,在同步塊中有兩個線程正在執行。

是我做錯了什么還是我錯過了什么?

它是否與場景背后的優化有關? 因為如果我使用'ii'鎖定一切都很完美。

此外,當使用'i'時,它確實打印'失敗'但是RARELY。

下面是用於運行程序的java版本。 java -version java version“1.7.0_51”Java(TM)SE運行時環境(版本1.7.0_51-b13)Java HotSpot(TM)客戶端VM(版本24.51-b03,混合模式,共享)

您可以使用以下程序運行程序來運行這一堆並查看結果。

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

這個

i++

相當於

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

換句話說, i現在引用一個不同的對象,與您最初同步的對象不同。

如果一個線程在i發生變化后碰巧到達synchronized塊,它也會進入,因為它在另一個對象上獲取了監視器。

因為如果我使用'ii'鎖定一切都很完美。

您沒有在任何地方更改對ii的引用。

這正是為什么鎖應該被聲明為final 這是一個如何用腳射擊自己的例子。

我認為問題在於你通過在synchronized塊中執行i++來改變'i'的值,這導致每次分配給i的對象都不同(在Eclipse中檢查它,查看變量的對象id之前並且在增量操作之后)。

Integer是一個不可變的類。 當你在它上面執行像++這樣的操作時,你就是在創建一個新實例。 這可能導致線程在不同對象上同步,使它們能夠同時訪問synchronized塊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM