2013-03-10 84 views
1

我試圖讓一個小程序來演示同步,但無論出於何種原因,這並不符合我的期望。重點是製作1000個線程,並讓它們全部爲靜態Integer對象「sum」加1。輸出應該是1000,但我得到不同的輸出。這就像addSum()方法根本不同步。我試過拖延println,認爲它打印速度太快,但這不是問題。 我在這裏錯過了什麼?Java同步不起作用

public class sumsync implements Runnable { 
public static Integer sum = new Integer(0); 
public sumsync(){ 
} 

private synchronized void addSum(int i){ 
    sum += i; 
} 

@Override 
public void run() { 
    addSum(1); 
} 
} 

主要類:

public class sumsyncinit { 

private static final int max_threads = 1000; 

public static void main(String[] args) { 

sumsync task = new sumsync(); 
Thread thread; 

    for(int i=0; i<max_threads;i++){ 
     thread = new Thread(task); 
     thread.start(); 
    } 
    System.out.println(sumsync.sum); 
} 

} 

回答

5

你是不是在等待你的線程來完成,因而沒有所有的增量已執行的保證。你基本上只是保證一次只有一個線程在addSum方法中。您可能需要使用Futures來等待結果。

+0

來自OP:_我曾嘗試延遲println,認爲它打印速度太快,但這不是問題。_ – bsiamionau 2013-03-10 14:34:34

+2

但是,並不保證所有線程都已執行。 – Stephan 2013-03-10 14:36:02

+0

我試圖在sysout之前添加Thread.sleep(1000),它工作。 OP是不正確的。 +1 – bsiamionau 2013-03-10 14:37:43

0

使用ThreadPoolExecutorhttp://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html)以先有一些統籌,而不是每次都創建一個新的線程,其次,調用爲了其awaitTermination打印方法,結果前等待所有線程終止。

實際上,在你的情況下,你還沒有設置一個機制來防止在所有線程完成它們的變量增量後發生打印執行。因此,打印結果可能是隨機的。awaitTermination在所有線程上充當join()並達到此要求。

此外,使變量volatile在這種情況下將是無用的和不安全的。

確實,​​關鍵字已經充當內存屏障和原子性,而易失性僅僅確保內存屏障。

此外,這裏的規則要記住,當一個人想做出一個變量volatile

可以使用volatile變量,而不是隻鎖定下 組受限制的情況。爲了提供期望的線程安全性,以下兩個條件必須滿足volatile變量:
寫入變量不取決於其當前值。
變量不參與其他變量的不變量。

+0

這並不能解釋爲什麼OP的SSCCE不能按預期運行。 – 2013-03-10 17:33:33

+0

@Matt Ball當我寫道:「爲了在打印結果之前等待所有線程終止,它就是暗示的。」實際上,打印執行可能發生在所有線程完成變量增量之前。我正在更新它更加精確。謝謝:) – Mik378 2013-03-10 17:36:45

-2

您還可以使sumAtomicInteger

然後addSum()看起來如下。

public static AtomicInteger sum = new AtomicInteger(0); 

private void addSum(int i){ 
    sum.addAndGet(i); 
} 

UPDATE:溶液上僅旨在解決爭用條件。在Stephen的評論下面我發佈完整的解決方案,以便主線程在打印最終值之前等待其他線程完成。

import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.atomic.AtomicInteger; 

public class sumsync implements Runnable { 
public static AtomicInteger sum = new AtomicInteger(0); 
private CountDownLatch latch; 
public sumsync(CountDownLatch latch){ 

    this.latch = latch; 
} 

private synchronized void addSum(int i){ 
    sum.addAndGet(i); 
} 

@Override 
public void run() { 
    addSum(1); 
    latch.countDown(); 
} 
} 

import java.util.concurrent.CountDownLatch; 

public class sumsyncinit { 

private static final int max_threads = 1000; 

public static void main(String[] args) { 

CountDownLatch latch = new CountDownLatch(max_threads); 

sumsync task = new sumsync(latch); 
Thread thread; 

    for(int i=0; i<max_threads;i++){ 
     thread = new Thread(task); 
     thread.start(); 
    } 
    try { 
     latch.await(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    System.out.println(sumsync.sum); 
} 

} 
+1

這並不能解決OP所要求的問題。它只消除了對'synchronized'的需求,但是你仍然遇到「總和不是1000個問題」 – Stephan 2013-03-10 14:41:03

+0

@Stephan - 使用AtomicInteger在這裏適當地處理競爭條件。因爲主線程不會等待線程結束,所以最後的1000不會被打印。這完全是一個不同的問題。這可以使用CountDownLatch來解決。我也爲此發佈瞭解決方案。 – sgp15 2013-03-11 03:29:45