2015-07-06 29 views
0

我使用while循環知道什麼時候該停止應用程序,我使用了線程的執行服務:While循環使用線程不會停止

ExecutorService executorService = Executors.newFixedThreadPool(8); 
String[] error = {""}; 
while(!(error[0].equals("Nothing found"))) { 
    executorService.execute(new Runnable() { 
     @Override 
     public void run() { 
      addProductsToSet(); 

      //rest of the code 
      synchronized (error[0]) { 
       error[0] = // code that might return "Nothing found" to stop the while loop 
      } 
      //rest of the code 
     } //end of run method 
    }); 
} 

executorService.shutdown(); 
while(!executorService.isTerminated()) { 
} 

,我的問題是在於error[0]有值「找不到」,但代碼不會停止。它永遠持續下去。如果我不使用線程,則while循環停止。我也嘗試executorService.shutdownNow(),但不起作用。

有什麼建議嗎?

+2

您是否嘗試使'error'變量易變? – Eran

+0

永遠不要寫一個旋轉循環。我強烈建議你用'executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.MILLISECONDS)'替換'while(!executorService.isTerminated())',就像[ExecutorService的文檔。shutdown()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#shutdown--)建議。 – VGR

回答

1

你的代碼的一個基本問題是你試圖在「任何對象是數組的第0個元素」上進行同步,但是這個元素本身可能會改變。另一個問題是當你嘗試讀取錯誤的狀態時,你根本就沒有同步任何東西。

如果您確實必須使用String作爲是否以這種方式繼續的標記,那麼解決方案是使用簡單的String變量,但將其聲明爲volatile,或者查看AtomicReference類。

這就是說,我強烈建議看看試圖改變你的程序流在這裏使用Callable接口,而不是Runnable。您將Callable傳遞給Executor.submit()方法,並且實際上,允許您執行的操作是讓線程中的任務「正確」將錯誤(作爲例外)傳回給發佈線程(運行循環的線程在這種情況下)。這樣,你會避免潛在的同步錯誤,我認爲你的程序流程會更清晰。

1

你的代碼有兩個問題。首先,您依賴的是從另一個線程可以看到error[0]的非易失性變量。你不能依靠它。 JVM可能會優化您的代碼以僅讀取error[0]一次,因爲它不能保證在另一個線程中的寫入將可見。請注意,數組元素始終是非易失性的,因此您無法使用數組正確執行此操作。正確的方法是,例如使用AtomicReference

AtomicReference<String> error = new AtomicReference<>(""); 
while (!(error.get().equals("Nothing found"))) { 
    executorService.execute(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println(i.incrementAndGet()); 
      addProductsToSet(); 
      // rest of the code 

      error.set("Nothing found"); 
      // rest of the code 
     } // end of run method 
    }); 
} 

雖然可能如果你只有兩種狀態(空字符串和"Nothing found"),然後AtomicBoolean將是最好的解決方案。當然,你不應該在AtomicReference上同步。

第二個問題是,您甚至在開始執行任務之前創建大量任務。你的while循環只是在沒有任何等待的情況下向池中提交任務,因此當至少有一個任務將被實際啓動並能夠說出"Nothing found"時,池中將有數千個任務,並且您將不得不等待所有任務完成shutdown()。您可以通過將shutdown()更改爲shutdownNow()來快速修復此問題,但我想這隻會隱藏原始問題。你必須做的實際事情是重新考慮你是否需要這麼多的任務。

0

整個模式看起來有點瑕疵。我建議這樣的模式:

ExecutorService executorService = Executors.newFixedThreadPool(4); 
executorService.execute(new Runnable() { 
    @Override 
    public void run() { 
     while(true) { 
      addProductsToSet(); 
      // rest of the code 

      String error = code that might return "Nothing found" to stop the while loop 
      if(error.equals("Nothing found")) { 
       break; 
      } 
     } 
     // rest of the code 
    } // end of run method 
}); 

executorService.shutdown(); 
while (!executorService.isTerminated()) { 
} 

藉助它,每個線程停止,如果該方法返回「沒有發現」,你不啓動在每個while循環迭代一個新的線程池。