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 等工具類,我們可以建立複雜的並行任務執行演算法,而且可以輕鬆改變執行緒數。
希望這篇短文能有助於你對併發程式設計的理解。