2015-11-07 106 views
2
package threadShareResource1; 

public class NonSynchro1 { 
    private int sum = 0; 

    public static void main(String[] args) { 
     NonSynchro1 n = new NonSynchro1(); 
     n.task(); 
     System.out.println(n.getSum()); 
    } 

    public synchronized void sumAddOne(){ 
     sum++; 
    } 

    public void task(){ 
     for (int i = 0; i < 100; i++) { 
      new Thread(new Runnable(){ 
       @Override 
       public void run() { 
        sumAddOne(); 
       } 
      }).start(); 

     /* try { 
       Thread.sleep(10); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } */ 
     } 
    } 
    public int getSum() { 
     return sum; 
    } 
} 

沒有註釋的代碼部分,程序有數據損壞,每次運行它時都不是100。但我認爲synchronized關鍵字應該獲得對sumAddOne方法的鎖定,該方法是我的程序的關鍵區域,允許一次線程訪問此方法。同步關鍵字不起作用

我試着使用ExecutorService,但它並沒有給出100個所有的運行。

public void task(){ 
    ExecutorService s = Executors.newCachedThreadPool(); 

    for (int i = 0; i < 100; i++) { 
     s.execute(new Thread(new Runnable(){ 
      @Override 
      public void run() { 
       sumAddOne(); 
      } 
     })); 
    } 
    s.shutdown(); 

    while(!s.isTerminated()){} 
} 
+0

預期行爲是什麼?你想達到什麼目的? –

+0

測試何時使用同步訪問公共變量的100個線程。所有的運行結果應該是100。 –

回答

4

在任務()中,您啓動100個線程(這是很多),每個線程將添加1到總和。

但是,當完成任務時,您所知道的是100個線程正處於啓動的某個過程中。你在調用println()之前不會阻塞,所以你怎麼知道所有的線程都已經完成?

睡眠可能「防止腐敗」,只是因爲它讓系統有時間完成啓動所有線程。

除此之外,您正在使用正確同步。任何地方多個線程可能會寫入您需要的相同變量,並且通常(簡化),如果您只是閱讀,則不需要它。

+0

你是完全正確的,當我使用'ExecutorService'時,我忘了使用'synchronized',這就是爲什麼結果不對。所以問題和你描述的一樣,謝謝。 –

+0

你非常歡迎韋斯利。看起來這只是學習的演示代碼,但爲了防萬一,我想指出100個線程很多,不應該用於生產代碼。您可以輕鬆地排出機器的螺紋。一般來說,如果您需要更多的話,您可以使用1個或「幾個」線程或移動到線程池:https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html –

3

同步關鍵字使用正確,問題在於您沒有等待線程完成。這是一個可能的解決方案:

public class NonSynchro1 { 

    private static final ExecutorService executorService = Executors.newCachedThreadPool(); 

    private int sum = 0; 

    public static void main(String[] args) { 
     NonSynchro1 n = new NonSynchro1(); 
     n.task(); 
     System.out.println(n.getSum()); 
     executorService.shutdown(); 
    } 

    public synchronized void sumAddOne() { 
     sum++; 
    } 

    public void task() { 
     List<Callable<Object>> callables = new ArrayList<>(); 
     for (int i = 0; i < 100; i++) { 
      callables.add(() -> { 
       sumAddOne(); 
       return null; 
      }); 
     } 

     List<Future<Object>> futures; 
     try { 
      futures = executorService.invokeAll(callables); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 

     futures.forEach(future -> { 
      try { 
       future.get(); 
      } catch (ExecutionException | InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     }); 
    } 

    public int getSum() { 
     return sum; 
    } 

} 

首先我們創建一個可調用列表 - 一個將並行執行的函數列表。

然後我們在執行器服務上調用它們。 newCachedThreadPool我已經在這裏使用了,默認情況下有0個線程,它會創建儘可能多的執行所有傳遞的可調用對象,這些線程會在閒置一分鐘後被終止。

最後,在for-each循環中我們解決了所有的問題。呼叫將阻塞,直到執行程序服務執行該功能。它也會拋出異常,如果它被拋入函數內(沒有調用get()你根本看不到這種異常)。

此外,當您想要優雅地終止程序時關閉執行程序服務是一個好主意。在這種情況下,主方法結束時只是executorService.shutdown()。如果你不這樣做,程序將在空閒線程死亡一分鐘後終止。但是,如果不同的執行程序服務,線程在空閒時可能不會被終止,在這種情況下,程序永遠不會終止。

+0

謝謝您的回答,我已經修好了 –

+1

很高興你把它修好了Wesley,我只想指出,這傢伙的策略非常好,因爲它使用線程池而不是直接管理線程。 –

+0

你可能應該在'ExecutorService'上調用'shutdown' –

0

只是爲了完整性的緣故:這裏顯示原來的程序如何,可向等待所有線程由joining他們完成一個解決方案:

 for (Thread t : n.task()) 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

要求task回到它創建線程。在這種情況下,我們不需要使緩存管理器或集合變得複雜:一個簡單的數組就可以。以下是完整的課程:

public class TestSynchro1 { 
    private int sum = 0; 

    public synchronized void sumAddOne() { 
     sum++; 
    } 

    public Thread[] task(int n) { 
     Thread[] threads = new Thread[n]; 
     for (int i = 0; i < n; i++) { 
      (threads[i] = new Thread(new Runnable() { 
       @Override 
       public void run() { 
        sumAddOne(); 
       } 
      })).start(); 
     } 
     return threads; 
    } 

    public static void main(String[] args) { 
     TestSynchro1 n = new TestSynchro1(); 
     for (Thread t : n.task(100)) 
      try { 
       t.join(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     System.out.println(n.sum); 
    } 
}