天天看點

java 線程傳回結果_JAVA 線程池之Callable傳回結果

本文介紹如何向線程池送出任務,并獲得任務的執行結果。然後模拟 線程池中的線程在執行任務的過程中抛出異常時,該如何處理。

一,執行具體任務的線程類

要想 獲得 線程的執行結果,需實作Callable接口。FactorialCalculator 計算 number的階乘,具體實作如下:

1 importjava.util.concurrent.Callable;2 importjava.util.concurrent.TimeUnit;3

4

7 public class FactorialCalculator implements Callable{8

9 privateInteger number;10

11 publicFactorialCalculator(Integer number) {12 this.number =number;13 }14 public Integer call() throwsException {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 returnresult;30 }31 }

上面23行--25行的if語句表明:如果number大于5,那麼 if(i==5)成立,會抛出異常。即模拟  執行5 以上的階乘時,會抛出異常。

二,送出任務的Main類

下面來看看,怎樣向線程池送出任務,并擷取任務的傳回結果。我們一共向線程池中送出了10個任務,是以建立了一個ArrayList儲存每個任務的執行結果。

第一行,首先建立一個線程池。第二行,建立List儲存10個線程的執行結果 所要存入的地方,每個任務是計算階乘,是以線程的傳回結果是 Integer。而這個結果隻要計算出來了,是放在Future裡面。

第5-7行,随機生成一個10以内的整數,然後建立一個 FactorialCalculator對象,該對象就是待執行的任務,然後在第8行 通過線程池的submit方法送出。

1 ThreadPoolExecutor executor =(ThreadPoolExecutor) Executors.newCachedThreadPool();2 List> resultList = new ArrayList>();3 Random random = newRandom();4 for (int i = 0; i < 10; i ++) {5 int rand = random.nextInt(10);6

7 FactorialCalculator factorialCalculator = newFactorialCalculator(rand);8 Future res = executor.submit(factorialCalculator);//異步送出, non blocking.

9 resultList.add(res);10 }

需要注意的是:submit方法是個非阻塞方法,參考這篇文章。送出了任務後,由線程池裡面的線程負責執行該任務,執行完成後得到的結果最終會儲存在 Future裡面,正如第8行所示。

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循環中,檢查任務的狀态---是否執行完成。

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 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,檢視它的狀态 isDone() ,即:是否執行完畢。

第13行,while結束條件:當所有的線程的執行完畢時,就退出do循環。注意:當線程在執行過程中抛出異常了,也表示線程執行完畢。

擷取線程的執行結果

1 System.out.println("Results as folloers:");2 for (int i = 0; i < resultList.size(); i++) {3 Future 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,第7行 從Future中獲得任務的執行結果。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()

java 線程傳回結果_JAVA 線程池之Callable傳回結果

但它不影響Main類線程---主線程的執行。

java 線程傳回結果_JAVA 線程池之Callable傳回結果

整個Main類代碼如下:

importjava.util.ArrayList;importjava.util.List;importjava.util.Random;import java.util.concurrent.*;

public classMain {public static voidmain(String[] args) {

ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool();

List> resultList = new ArrayList>();

Random random= newRandom();for (int i = 0; i < 10; i ++) {int rand = random.nextInt(10);

FactorialCalculator factorialCalculator= newFactorialCalculator(rand);

Future 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 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()

System.out.println("Results as folloers:");for (int i = 0; i < resultList.size(); i++) {

Future 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。此時,就可以對抛出異常的任務“進行處理”。此外,線程池中執行該任務的線程,并不會因為 該任務在執行過程中抛出了異常而受到影響,該線程 可以繼續 接收并運作 下一次送出給它的任務。

圖中 task1、task2、task4 傳回的結果為空,表明它們抛出了IllegalArgumentException異常。

參考資料

《Java 7 Concurrency Cookbook》 chapter 4

原文:http://www.cnblogs.com/hapjin/p/7599189.html