2013-11-04 110 views
6

對不起,如果這是非常明顯的或已在其他地方回答。我一直無法找到任何東西。我有以下代碼:Java同步混淆

public class SimpleThread extends Thread { 
    public static Integer sharedVal = 0; 
    public SimpleThread() { 

    } 
    @Override 
    public void run() { 
     while(true) {   
      iterator(); 
     } 
    } 

    public void theSleeper() { 
     System.out.println("Thread: " + this.getId() + " is going to sleep!"); 
     try { 
      this.sleep(5000); 
     } catch(Exception e) { 

     } 
    } 

    public void iterator() { 
     synchronized(sharedVal) { 
      System.out.println("Iterating sharedVal in thread: " + this.getId()); 
      sharedVal++; 
      System.out.println(sharedVal); 
      theSleeper(); 
      System.out.println("Thread : " + getId() + " is done sleeping, trying to iterate again..."); 
     } 
    } 
} 

我創建了這個SimpleThread類的兩個實例,並執行run方法。我期望看到像這樣的東西:線程9遞增...線程9睡眠...(5秒後)線程10遞增....線程10睡眠...。我期望這是因爲我鎖定了迭代器方法,以便一次只能有一個線程能夠輸入它。相反,兩個線程都會增加,然後等待5秒鐘。這永遠重複。我在這裏錯過了什麼,以便得到預期的行爲?非常感謝!

編輯:我做了一個新的公共靜態變量:public static Object theLock = new Object()。現在,在迭代器方法中,我確實同步了(theLock)。現在輸出更像我所期望的那樣,因爲鎖永遠不會改變。但是,現在只有線程9進入該方法。看來,線程10正在捱餓,並且永遠不會回合。這似乎很奇怪。這不只是幾次,它總是隻是線程9迭代和睡眠。我會認爲這將是9,10,9,10。或者可能是一個隨機分佈像9,10,10,10,9,10,9,9,10等

編輯2:我看到什麼是正在發生。線程9有鎖。線程10嘗試進入該功能,但被立即告知要等待。函數完成後,它可能仍然是線程9轉。線程9然後獲得鎖,並且循環繼續。線程10得到一個回合的時間窗口非常小,如果它確實得到了回合,它可能會捱餓9.也就是說,在iterator()中的synchronized塊之後放置yield()看起來並沒有使它更多公平。我閱讀了關於該方法的評論,而調度器實際上可以忽略yield()。

+3

自動裝箱是魔鬼 –

+0

哈哈哈,現在看來,這可能是有問題的! :) – user2045279

回答

3

你的問題所在位置:

sharedVal++; 

那行,由於自動裝箱轉換成這樣:

sharedVal = Integer.valueOf(sharedVal.intValue() + 1); 

它在每次運行時都會創建一個新的Integer對象,因此每次到達​​塊時都會鎖定一個不同的價值。

使用專用的對象同步:

private final static Object LOCK = new Object(); 

,然後更改同步使用synchronized (LOCK) {...代替。

(你也可以使用getClass()鎖定,但我個人不喜歡暴露鎖定對象,以公共世界)

+0

是的,謝謝你指出我的錯誤。請參閱我的編輯,謝謝! – user2045279

+0

當線程9從睡眠中回來時,它釋放鎖並重新獲取它,從而使線程10不可能。它唯一的機會是調度程序在鎖之間停止線程9,這是不太可能的。在迭代之間添加一個'Thread.yield()',這會讓事情變得更加公平。 – Darkhogg

+0

是的,我現在看到了,並將其添加到我的第二次編輯。 Thread.yield()似乎沒有多大幫助,因爲調度程序可能忽略了它。話雖如此,我認爲我現在瞭解更多。非常感謝! – user2045279

5

當你正在增加你創建一個新的整數實例和一個新的對象進行鎖定。

+0

^- - - - 這。 – MadConan

+1

@OP:再次嘗試使用一個可變的東西,就像一個StringBuilder(不是StringBuffer,因爲StringBuffer是同步的)。 – MadConan

+0

我完全同意,並且我覺得很愚蠢。我不知道爲什麼Integer是不可改變的,但那是另一個問題。請參閱我的編輯。 – user2045279

1

您有效地改變該線程使用在每個迭代上的鎖:++整型創建一個新的實例(Integer類是不可變的),當您執行sharedVal ++

+0

是的,另一個提到,以及指出這個錯誤。請參閱我的編輯 – user2045279

0

sharedVal成員varible情況正在發生變化。相反,你可以使用同步下面的語句:

同步(SympleThread.class)