2016-03-23 21 views
1

我試圖用CyclicBarrier重新實現我的併發代碼,這對我來說是新的。我可以沒有,但我的時間試用這對我的其他的解決方案,這個問題我已經是用下面的代碼死鎖情況:爲什麼`ExecutorService`不能一致地調度線程?

//instance variables (fully initialised elsewhere). 
private final ExecutorService exec = Executors.newFixedThreadPool(4); 
private ArrayList<IListener> listeners = new ArrayList<IListener>(); 
private int[] playerIds; 

private class WorldUpdater { 
    final CyclicBarrier barrier1; 
    final CyclicBarrier barrier2; 
    volatile boolean anyChange; 
    List<Callable<Void>> calls = new ArrayList<Callable<Void>>(); 

    class SyncedCallable implements Callable<Void> { 
     final IListener listener; 

     private SyncedCallable(IListener listener) { 
      this.listener = listener; 
     } 

     @Override 
     public Void call() throws Exception { 
      listener.startUpdate(); 
      if (barrier1.await() == 0) { 
       anyChange = processCommons(); 
      } 
      barrier2.await(); 
      listener.endUpdate(anyChange); 
      return null; 
     } 
    } 

    public WorldUpdater(ArrayList<IListener> listeners, int[] playerIds) { 
     barrier2 = new CyclicBarrier(listeners.size()); 
     barrier1 = new CyclicBarrier(listeners.size()); 
     for (int i : playerIds) 
      calls.add(new SyncedCallable(listeners.get(i))); 
    } 

    void start(){ 
     try { 
      exec.invokeAll(calls); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 


void someMethodCalledEveryFrame() { 
    //Calls some Fisher-something method that shuffles int[] 
    shufflePIDs(); 
    WorldUpdater updater = new WorldUpdater(listeners, playerIds); 
    updater.start(); 
} 

我使用調試器Android Studio中(的IntelliJ)在這個暫停執行階段。我得到的多線程顯示我的await電話爲我最後的代碼被執行

- >Unsafe.park

- >LockSupport.park

- >AbstractQueuedSynchronizer$ConditionObject.await

- >CyclicBarrier.doWait

- >CyclicBarrier.await

至少一個線程將是有這樣的堆棧:

- >Unsafe.park

- >LockSupport.park

- >AbstractQueuedSynchronizer$ConditionObject.await

- >LinkedBlockingQueue.take

- >ThreadPoolExecutor.getTask

- >ThreadPoolExecutor.runWorker

- >ThreadPoolExecutor$Worker.run

- >Thread.run

我注意到CyclicBarrier在這些後面的雜散線程中不起作用。

processCommons調用exec.invokeAll(在3個聽衆),我想這意味着我跑出來的線程。但很多時候這不會發生,所以請有人澄清爲什麼ExecutorService不能一致地安排我的線程?他們有自己的堆棧和程序計數器,所以我會認爲這不成問題。我只有一次運行max 4。有人幫助我瞭解數學嗎?

回答

0

我做了一個小實驗,並想出了:

 @Override 
     public Void call() throws Exception { 
      System.out.println("startUpdate, Thread:" + Thread.currentThread()); 
      listener.startUpdate(); 
      if (barrier1.await() == 0) { 
       System.out.println("processCommons, Thread:" + Thread.currentThread()); 
       anyChange = processCommons(); 
      } 
      barrier2.await(); 
      System.out.println("endUpdate, Thread:" + Thread.currentThread()); 
      listener.endUpdate(anyChange); 
      return null; 
     } 

使用的3池3 listeners時顯露,我會一直在processCommons掛其中包含以下內容:

List<Callable<Void>> calls = new ArrayList<Callable<Void>>(); 
    for (IListener listiner : listeners) 
     calls.add(new CommonsCallable(listener)); 
    try { 
     exec.invokeAll(calls); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

有2個線程在屏障等待,第三個嘗試創建3個線程。在ExecutorService中我需要一個額外的線程,屏障上的2可以「回收」,正如我在問我的問題。在這個階段,我有6條線索的參考,exec只持有4個。這可以在很多分鐘內愉快地運行。

private final ExecutorService exec = Executors.newFixedThreadPool(8); 

應該會更好,但事實並非如此。

最後,我在的IntelliJ(感謝ideaC!)沒有斷點步進

問題是

 if (barrier1.await() == 0) { 
      anyChange = processCommons(); 
     } 
     barrier2.await(); 

之間的2 await你可能會得到一些暫停線程實際上還沒有達到await。在3個聽衆中有4個聽衆的情況下,只需要一個聽到「不定期」(或其他),barrier2就不會得到全部補充。但是當我擁有8個游泳池時呢?除了兩個線程之外,所有線程都會顯示相同的行爲:「不安全」公園。

- > LockSupport.park

- >的AbstractQueuedSynchronizer $ ConditionObject.await

- > LinkedBlockingQueue.take

- > ThreadPoolExecutor.getTask

- > ThreadPoolExecutor.runWorker

- > ThreadPoolExecutor $ Worker.run

- > Thread.run

這裏可能會發生什麼情況來禁用所有5個線程?我應該採取詹姆斯大的建議,並避免在這個詳盡的CyclicBarrier crowbarring。 - 更新 - 它可以現在運行一整夜現在沒有CyclicBarrier

1

當您的WorldUpdater創建時,listeners.size()的值是多少?如果它超過四個,那麼你的線程將永遠不會超越屏障。

您的ExecutorService已有正好四個線程。沒有更多,沒有更少。 barrier1.await()barrier2.await()的調用者在線程正在等待listeners.size()之前不會超過屏障。

我的直覺反應是,泳池線程使用CyclicBarrier將是一個錯誤。 CyclicBarrier僅在您確切知道將使用多少個線程時纔有用。但是,當你使用線程池時,你經常做而不是知道池的大小。事實上,在真實世界(即商業)應用程序中,如果您正在使用線程池,則可能完全不是由您的代碼創建的。它可能是在其他地方創建的,並作爲注入的依賴項傳遞給您的代碼。

+0

我最初批准這個是因爲你強調「_exactly_ four」,「不多,不少」,這看起來很深刻。我現在意識到這只是錯誤的:_like_我嘗試使用'CyclicBarrier'。它只是不適合這裏,所以感謝那個指針。 – John

相關問題