2010-10-21 98 views

回答

38

ExecutorService提取許多與較低級別抽象(如raw Thread)相關的複雜性。它提供了在成功或突然終止任務時安全啓動,關閉,提交,執行和阻止的機制(表示爲RunnableCallable)。

JCiP,第6.2節,直接從馬的嘴:

Executor可能是一個簡單的界面,但它形成了支持多種任務的異步任務執行靈活而強大的框架的基礎執行政策。它提供了一個標準的解耦方法 任務提交任務執行,描述任務爲 RunnableExecutor實現還提供生命週期支持和掛鉤以添加統計信息收集,應用程序管理和監視。 ... 使用Executor通常是在您的應用程序中實施生產者 - 消費者設計的最簡單途徑。

與其花費你的時間實現(通常不正確,並以極大的努力)的底層基礎架構的並行性,j.u.concurrent框架允許你轉而專注於結構化的任務,依賴,潛在的並行性。對於大量的併發應用程序,可以直接識別和利用任務邊界,並利用j.u.c,使您可以專注於可能需要更專業解決方案的真正併發挑戰的小部分。

而且,儘管樣板的外觀和手感,Oracle API page summarizing the concurrency utilities包括一些非常堅實的論據使用它們,並非最不重要的:

開發者可能已經 理解標準庫 班,所以沒有需要學習 併發組件的API和行爲特設 。另外, 併發應用程序遠在 上時更易於調試,因爲它們是在可靠,經過充分測試的組件上構建的 。

這個question on SO詢問一本好書,JCiP的直接答案就是這樣。如果你還沒有,給自己一個副本。在那裏提出的綜合併發方法遠遠超出了這個問題,並且從長遠來看將爲您節省很多心痛。

18

我看到的一個優點是管理/調度幾個線程。使用ExecutorService,您不必編寫自己的線程管理器,它可能會受到bug的困擾。如果你的程序需要一次運行多個線程,這是特別有用的。比如你想在同一時間執行兩個線程,你可以很容易地做到這一點是這樣的:

ExecutorService exec = Executors.newFixedThreadPool(2); 

exec.execute(new Runnable() { 
    public void run() { 
    System.out.println("Hello world"); 
    } 
}); 

exec.shutdown(); 

的例子可能是微不足道的,但儘量以爲「世界你好」行由一個沉重的工作和您希望該操作一次在多個線程中運行,以提高程序的性能。這只是一個例子,還有很多情況下您想要安排或運行多個線程並使用ExecutorService作爲您的線程管理器。

對於運行單線程,我沒有看到使用ExecutorService的任何明顯優勢。

+0

是不是exec.execute(new Runnable()..? – devnull 2010-10-21 03:42:00

+0

因爲Thread實現了Runnable,所以兩者都沒關係,對於簡單的情況,Runnable應該是足夠的 – Manny 2010-10-21 03:56:48

+1

我真的不認爲創建'當你所需要的只是一個'Runnable'時......你甚至不會啓動'Thread',所以它只會增加混淆和不必要的包袱。 – ColinD 2010-10-21 04:00:47

9

要通過執行Runnable接口來運行任務,我們必須創建Thread的對象,如new thread(RunnableObject).start()。但是我們知道創建線程有自己的開銷並存儲在堆棧和堆內存中。創建一個線程對象只需要在單獨的線程中運行任務是非常昂貴的。

Executors框架(java.util.concurrent.Executor),由Java 5發佈的包java.util.concurrent用於運行Runnable線程對象而不創建Thread對象。

Executor框架是根據一組執行策略標準化調用,調度,執行和控制異步任務的框架。」

9

下面是一些好處:

  1. 執行人服務異步方式
  2. 使用調用來獲得線程完成後返回結果管理線程。
  3. 管理工作,空閒線程和線程轉售完成的工作分配自動分配新的工作
  4. 叉 - join框架進行並行處理
  5. 線程
  6. 的invokeAll和invokeAny給予更多的控制運行任何之間
  7. 更好的溝通或全部線程同時
  8. 關閉所有線程分配的工作
  9. 安排執行程序服務用於生產可運行的重複調用提供方法的完成提供能力,可調用 希望它會幫助你
+1

在第二點而不是Callable中,它不是一個「未來」嗎?未來是線程完成後我們可以從哪裏獲取結果/值。 – AmitG 2017-05-04 04:58:38

+0

是的,例如。未來 future = executorService.submit(可調用); – 2018-03-02 18:01:14

1

此前Java 1.5的版本,主題/ Runnable接口被設計爲兩個獨立的服務

單位工作的
  1. 工作單元的執行

ExecutorService的解耦那些通過將Runnable/Callable指定爲工作單元並將Executor指定爲執行(使用生命週期)工作單元的兩種服務

1

ExecutorService還允許訪問FutureTask,FutureTask將返回調用類一旦完成後臺任務的結果。在由執行人框架克服了傳統的主題實現的可贖回

public class TaskOne implements Callable<String> { 

@Override 
public String call() throws Exception { 
    String message = "Task One here. . ."; 
    return message; 
    } 
} 

public class TaskTwo implements Callable<String> { 

@Override 
public String call() throws Exception { 
    String message = "Task Two here . . . "; 
    return message; 
    } 
} 

// from the calling class 

ExecutorService service = Executors.newFixedThreadPool(2); 
    // set of Callable types 
    Set<Callable<String>>callables = new HashSet<Callable<String>>(); 
    // add tasks to Set 
    callables.add(new TaskOne()); 
    callables.add(new TaskTwo()); 
    // list of Future<String> types stores the result of invokeAll() 
    List<Future<String>>futures = service.invokeAll(callables); 
    // iterate through the list and print results from get(); 
    for(Future<String>future : futures) { 
     System.out.println(future.get()); 
    } 
5

以下限制的情況下(內置線程池框架)。

  • 不良資源管理即它不斷爲每個請求創建新的資源。創建資源沒有限制。使用Executor框架,我們可以重用現有資源並限制創建資源。
  • 不是魯棒的:如果我們繼續創建新的線程,我們將得到StackOverflowException異常,因此我們的JVM將崩潰。
  • 開銷創建時間:對於每個請求,我們需要創建新的資源。創建新資源非常耗時。即線程創建>任務。使用Executor框架,我們可以建立在線程池中。線程池的

優勢

  • 線程池的使用由請求或任務處理過程中避免線程創建減少響應時間。

  • 使用線程池可以根據需要更改執行策略。您可以通過替換ExecutorService實現從單線程轉到多線程。

  • Java應用程序中的線程池通過根據系統負載和可用資源創建配置的線程數來增加系統的穩定性。

  • 線程池將應用程序開發人員從線程管理中解放出來,並允許專注於業務邏輯。

Source

1

難道真的那麼昂貴,創建一個新的線程?

作爲一個基準,我只用Runnable s創建了60,000個線程,並使用了空的run()方法。創建每個線程後,我立即調用它的start(..)方法。這花了大約30秒的激烈CPU活動。響應於this question已經進行了類似的實驗。這些總結是,如果線程沒有立即完成,並且積累了大量活動線程(數千),那麼會出現問題:(1)每個線程都有一個堆棧,所以你將用盡內存,(2)操作系統每個進程的線程數可能有限制,但not necessarily, it seems

所以,據我所知,如果我們正在談論啓動每秒10個線程,並且它們都比新啓動更快完成,並且我們可以保證這個速率不會超過太多,那麼ExecutorService在可見性或穩定性方面不提供任何具體優勢。 (儘管在代碼中表達某些併發想法可能會更方便或更易讀)。另一方面,如果您可能每秒調度數百或數千個任務,需要花費時間運行,您可能會遇到大問題馬上。這可能會意外發生,例如如果您創建線程來響應對服務器的請求,並且服務器收到的請求強度有所增加。但例如響應每個用戶輸入事件(按鍵,鼠標移動)的一條線程似乎是非常好的,只要任務很簡短。