本文介紹如何向線程池提交任務,並得到任務的執行結果。而後模擬 線程池中的線程在執行任務的過程當中拋出異常時,該如何處理。html
一,執行具體任務的線程類java
要想 得到 線程的執行結果,需實現Callable接口。FactorialCalculator 計算 number的階乘,具體實現以下:segmentfault
1 import java.util.concurrent.Callable; 2 import java.util.concurrent.TimeUnit; 3 4 /** 5 * Created by Administrator on 2017/9/26. 6 */ 7 public class FactorialCalculator implements Callable<Integer> { 8 9 private Integer number; 10 11 public FactorialCalculator(Integer number) { 12 this.number = number; 13 } 14 public Integer call() throws Exception { 15 int result = 1; 16 17 if (number == 0 || number == 1) { 18 result = 1; 19 }else { 20 for (int i = 2; i < number; i++) { 21 result *= i; 22 TimeUnit.MICROSECONDS.sleep(200); 23 if (i == 5) { 24 throw new IllegalArgumentException("excepion happend");//計算5以上的階乘都會拋出異常. 根據須要註釋該if語句 25 } 26 } 27 } 28 System.out.printf("%s: %d\n", Thread.currentThread().getName(), result); 29 return result; 30 } 31 }
上面23行--25行的if語句代表:若是number大於5,那麼 if(i==5)成立,會拋出異常。即模擬 執行5 以上的階乘時,會拋出異常。app
二,提交任務的Main類dom
下面來看看,怎樣向線程池提交任務,並獲取任務的返回結果。咱們一共向線程池中提交了10個任務,所以建立了一個ArrayList保存每一個任務的執行結果。異步
第一行,首先建立一個線程池。第二行,建立List保存10個線程的執行結果 所要存入的地方,每一個任務是計算階乘,所以線程的返回結果是 Integer。而這個結果只要計算出來了,是放在Future<Integer>裏面。async
第5-7行,隨機生成一個10之內的整數,而後建立一個 FactorialCalculator對象,該對象就是待執行的任務,而後在第8行 經過線程池的submit方法提交。ide
1 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); 2 List<Future<Integer>> resultList = new ArrayList<Future<Integer>>(); 3 Random random = new Random(); 4 for (int i = 0; i < 10; i ++) { 5 int rand = random.nextInt(10); 6 7 FactorialCalculator factorialCalculator = new FactorialCalculator(rand); 8 Future<Integer> res = executor.submit(factorialCalculator);//異步提交, non blocking. 9 resultList.add(res); 10 }
須要注意的是:submit方法是個非阻塞方法,參考這篇文章。提交了任務後,由線程池裏面的線程負責執行該任務,執行完成後獲得的結果最終會保存在 Future<Integer>裏面,正如第8行所示。wordpress
As soon as we invoke the submit() method of ExecutorService the Callable are handed over to ExecutorService to execute.
Here one thing we have to note, the submit() is not blocking.
So, all of our Callables will be submitted right away to the ExecutorService, and ExecutorService will decide when to execute which callable.
For each Callable we get a Future object to get the result later.
接下來,咱們在do循環中,檢查任務的狀態---是否執行完成。oop
1 do { 2 // System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount()); 3 for (int i = 0; i < resultList.size(); i++) { 4 Future<Integer> result = resultList.get(i); 5 System.out.printf("Task %d : %s \n", i, result.isDone()); 6 } 7 try { 8 TimeUnit.MILLISECONDS.sleep(50); 9 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 } while (executor.getCompletedTaskCount() < resultList.size());
第3-6行for循環,從ArrayList中取出 每一個 Future<Integer>,查看它的狀態 isDone() ,即:是否執行完畢。
第13行,while結束條件:當全部的線程的執行完畢時,就退出do循環。注意:當線程在執行過程當中拋出異常了,也表示線程執行完畢。
獲取線程的執行結果
1 System.out.println("Results as folloers:"); 2 for (int i = 0; i < resultList.size(); i++) { 3 Future<Integer> result = resultList.get(i); 4 Integer number = null; 5 6 try { 7 number = result.get();// blocking method 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } catch (ExecutionException e) { 11 e.printStackTrace(); 12 } 13 System.out.printf("task: %d, result %d:\n", i, number); 14 }
第3行取出每一個存儲結果的地方:Future<Integer>,第7行 從Future<Integer>中得到任務的執行結果。Future.get方法是一個阻塞方法。但前面的do-while循環裏面,咱們已經檢查了任務的執行狀態是否完成,所以這裏可以很快地取出任務的執行結果。
We are invoking the get() method of Future to get the result. Here we have to remember that, the get() is a blocking method.
任務在執行過程當中,若拋出異常,下面語句在獲取執行結果時會直接跳到catch(ExecutionException e)中。
number = result.get()
但它不影響Main類線程---主線程的執行。
整個Main類代碼以下:
import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.*; /** * Created by Administrator on 2017/9/26. */ public class Main { public static void main(String[] args) { ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); List<Future<Integer>> resultList = new ArrayList<Future<Integer>>(); Random random = new Random(); for (int i = 0; i < 10; i ++) { int rand = random.nextInt(10); FactorialCalculator factorialCalculator = new FactorialCalculator(rand); Future<Integer> res = executor.submit(factorialCalculator);//異步提交, non blocking. resultList.add(res); } // in loop check out the result is finished do { // System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount()); for (int i = 0; i < resultList.size(); i++) { Future<Integer> result = resultList.get(i); System.out.printf("Task %d : %s \n", i, result.isDone()); } try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } while (executor.getCompletedTaskCount() < resultList.size()); System.out.println("Results as folloers:"); for (int i = 0; i < resultList.size(); i++) { Future<Integer> result = resultList.get(i); Integer number = null; try { number = result.get();// blocking method } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.printf("task: %d, result %d:\n", i, number); } executor.shutdown(); } }
在上面 number = result.get(); 語句中,咱們 catch了兩個異常,一個是InterruptedException,另外一個是ExecutionException。由於咱們是在main線程內得到任務的執行結果,main線程執行 result.get()會阻塞,若是在阻塞的過程當中被(其餘線程)中斷,則拋出InterruptedException。
若在任務的執行過程當中拋出了異常(好比IllegalArgumentException),那麼main線程在這裏就會catch到ExecutionException。此時,就能夠對拋出異常的任務「進行處理」。此外,線程池中執行該任務的線程,並不會由於 該任務在執行過程當中拋出了異常而受到影響,該線程 能夠繼續 接收並運行 下一次提交給它的任務。
圖中 task一、task二、task4 返回的結果爲空,代表它們拋出了IllegalArgumentException異常。
而要想對異常進行處理,可參考:Java線程池異常處理最佳實踐 和 這篇文章。
參考資料
《Java 7 Concurrency Cookbook》 chapter 4
原文:http://www.cnblogs.com/hapjin/p/7599189.html