1. 程式人生 > >Java 併發:Executors 和執行緒池

Java 併發:Executors 和執行緒池

讓我們開始來從入門瞭解一下 Java 的併發程式設計。

本文主要介紹如何開始建立執行緒以及管理執行緒池,在 Java 語言中,一個最簡單的執行緒如下程式碼所示:

Runnable runnable = new Runnable(){
   public void run(){
      System.out.println("Run");
   }
}

可通過下面一行程式碼來啟動這個執行緒:

new Thread(runnable).start();

這是一個再簡單不過的例子了,但如果你有許多需要長時間執行的任務同時執行,並需要等所有的這些執行緒都執行完畢,還想得到一個返回值,那麼這就有點小小難度了。但 Java 已經有解決方案給你,那就是 Executors ,一個簡單的類可以讓你建立執行緒池和執行緒工廠。

一個執行緒池使用類 ExecutorService 的例項來表示,通過 ExecutorService 你可以提交任務,並進行排程執行。下面列舉一些你可以通過 Executors 類來建立的執行緒池的型別:

  • Single Thread Executor : 只有一個執行緒的執行緒池,因此所有提交的任務是順序執行,程式碼: Executors.newSingleThreadExecutor()
  • Cached Thread Pool : 執行緒池裡有很多執行緒需要同時執行,老的可用執行緒將被新的任務觸發重新執行,如果執行緒超過60秒內沒執行,那麼將被終止並從池中刪除,程式碼:Executors.newCachedThreadPool()
  • Fixed Thread Pool : 擁有固定執行緒數的執行緒池,如果沒有任務執行,那麼執行緒會一直等待,程式碼: Executors.newFixedThreadPool()
  • Scheduled Thread Pool : 用來排程即將執行的任務的執行緒池,程式碼:Executors.newScheduledThreadPool()
  • Single Thread Scheduled Pool : 只有一個執行緒,用來排程執行將來的任務,程式碼:Executors.newSingleThreadScheduledExecutor()

一旦你建立了一個執行緒池,你就可以往池中通過不同的方法提交執行任務,可提交 Runnable 或者 Callable 到執行緒池中,該方法返回一個 Future 例項表示任務的狀態,如果你提交一個 Runnable ,那麼如果任務完成後 Future 物件返回 null。

例如,你編寫下面的 Callable:

private final class StringTask implements Callable<String>{
   public String call(){
      //Long operations

      return "Run";
   }
}

如果你想使用4個執行緒來執行這個任務10次,那麼程式碼如下:

ExecutorService pool = Executors.newFixedThreadPool(4);

for(int i = 0; i < 10; i++){
   pool.submit(new StringTask());
}

但你必須手工的關閉執行緒池來結束所有池中的執行緒:

pool.shutdown();

如果你不這麼做,JVM 並不會去關閉這些執行緒;另外你可以使用 shutdownNow() 的方法來強制關閉執行緒池,那麼執行中的執行緒也會被中斷,所有尚未被執行的任務也將不會再執行。

但這個例子中,你無法獲取任務的執行狀態,因此我們需要藉助 Future 物件:

ExecutorService pool = Executors.newFixedThreadPool(4);

List<Future<String>> futures = new ArrayList<Future<String>>(10);

for(int i = 0; i < 10; i++){
   futures.add(pool.submit(new StringTask()));
}

for(Future<String> future : futures){
   String result = future.get();

   //Compute the result
}

pool.shutdown();

不過這段程式碼稍微有點複雜,而且有不足的地方。如果第一個任務耗費非常長的時間來執行,然後其他的任務都早於它結束,那麼當前執行緒就無法在第一個任務結束之前獲得執行結果,但是彆著急,Java 為你提供瞭解決方案——CompletionService。

一個 CompletionService 就是一個服務,用以簡化等待任務的執行結果,實現的類是 ExecutorCompletionService,該類基於 ExecutorService,因此我們可試試下面的程式碼:

ExecutorService threadPool = Executors.newFixedThreadPool(4);
CompletionService<String> pool = new ExecutorCompletionService<String>(threadPool);

for(int i = 0; i < 10; i++){
   pool.submit(new StringTask());
}

for(int i = 0; i < 10; i++){
   String result = pool.take().get();

   //Compute the result
}

threadPool.shutdown();
通過這段程式碼,我們可以根據執行結束的順序獲取對應的結果,而無需維護一個 Future 物件的集合。

這就是本文的全部,通過 Java 為我們提供的各種工具,可以方便的進行多工的程式設計,通過使用 Executors、ExecutorService 以及 CompletionService 等工具類,我們可以建立複雜的並行任務執行演算法,而且可以輕鬆改變執行緒數。

希望這篇短文能有助於你對併發程式設計的理解。