2014-01-10 84 views
1

我看不出最終的計數器值爲20000.這段代碼有什麼問題?方法同步線程問題

public class Synchronize2 { 

    public static void main(String[] args) { 
     Threading t1 = new Threading(); 
     Threading t2 = new Threading(); 

     t1.start(); 
     t2.start(); 

     try { 
      t1.join(); 
      t2.join(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     System.out.println(Threading.counter); 
    } 
} 


class Threading extends Thread { 

    static int counter; 

    public synchronized void incrementer() { 
     counter++; 
    } 

    public void run() { 
     for (int i=0; i<10000; i++) { 
      incrementer(); 
     } 
    } 
} 
+2

您正在同步兩個不同的對象。 – SLaks

+0

@SLaks。增量值字段是靜態的 –

+1

@MarkusMalkusch:正確。他的代碼不是線程安全的。 – SLaks

回答

3

您同步的incrementer方法將鎖定對象本身。但是你有兩個不同的對象,每個對象都自己鎖定,所以這個方法不是線程安全的;兩個線程仍然可以同時訪問incrementer

此外,後增加操作不是線程安全的,因爲它不是原子的;有一個讀操作和一個增量操作,並且一個線程可以在兩個操作中間被中斷。這個非線程安全的代碼提供了一個競爭條件,其中線程一讀取值,線程二讀取值,然後線程一個增量和線程兩個增量,但只有最後一個增量「贏」和一個增量丟失。這顯示了當末端值小於20000

使該方法static過,所以因爲它的​​,它將類,它是在這裏適當的同步的類對象上的鎖。

public static synchronized void incrementer() { 
+2

或者使用'AtomicInteger'並完全放棄'synchronized'。 –

+0

可能很好解釋,因爲它們沒有鎖定在同一個對象上,所以++不是線程安全的。如果他改變爲靜態方法,那麼這將解決線程安全問題。 – Gray

0

您在兩個不同的對象上同步。你的提取器是這樣的一個簡短形式:

public void incrementer() { 
     synchronized (this) { 
      counter++; 
     } 
    } 

但是「this」的兩個實例不是相同的Object。因此,你根本不同步。試試這個方法:

private static Object sync = new Object(); 

    public void incrementer() { 
     synchronized (sync) { 
      counter++; 
     } 
    } 

你也應該使變量計數器易變。這裏不是必須的,因爲你只在同步塊中使用它。但在實際的代碼中,你可能會在這個塊之外讀取它,然後你會遇到問題。非易失性變量可以從本地線程緩存中讀取,而不是從內存讀取。

+0

如果你正在走下'synchronized'和'volatile'路線,那麼我強烈建議使用'AtomicInteger' - 這很可能在稍後導致問題。 –

+0

是的,你當然是對的。我認爲這是一個關於線程的問題,而不是關於遞增整數。因此,我專注於線程部分的答案 – Matthias