一、實現線程的幾種方式
初級階段我們創建線程主要有兩種方法:一種是直接繼承Thread類,一種是實現Runnable接口,但是這兩種方法都無法返回執行結果;如果需要獲取執行結果,就必須通過共享變量或者使用線程通信的方式來達到效果,實現起來比較麻煩。
在java1.5之後,就提供了Callable和Future,通過這兩種方法可以在執行結束後返回執行結果。
二、管理併發線程的返回結果
如果在併發執行的任務中,並且每個任務之後都需要獲取結果,有兩種方式可以實現:
第一種:通過一個list保存一組future,循環查看結果,future不一定完成,如果沒有完成,則調用get會發生阻塞;這樣如果排在前面的任務沒有完成,就會發生阻塞,後面已經完成的任務就無法獲取結果了,例如:
public static void test1() throws ExecutionException,InterruptedException{
ExecutorService exe = Executors.newFixedThreadPool(5);
List<Future<String>> result = new ArrayList<>();
Random random = new Random();
for(int i = 0 ; i < 10 ; i++){
result.add( exe.submit(() -> {
Thread. sleep(random.nextInt(10000));
return Thread.currentThread().getName();
}));
}
System. out.println("begin to get result:" );
int count = 0;
for(Future<String> f : result ){
System. out.println(f .get());
count++;
}
System. out.println("task has done:" +count );
exe .shutdown();
}
改進:future獲取結果之前先判斷future是否執行完畢(f.isDone()),如果執行完成,獲取結果之後,則從執行列表中刪除任務。
第二種方法:使用ExecutorCompletionService來管理線程池執行任務的執行結果,例如:
public static void test2() throws ExecutionException,InterruptedException{
ExecutorService exe = Executors.newFixedThreadPool(5);
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<>(exe );
Random random = new Random();
for(int i = 0 ; i < 10 ; i++){
ecs.submit(() -> {
Thread. sleep(random.nextInt(10000));
return Thread.currentThread().getName ();
});
}
System. out.println("begin to get result" );
int count = 0;
for(int i = 0 ; i < 10 ; i++){
Future<String> f = ecs.take();
System. out.println(f .get());
count++;
}
System. out.println("task has done:" +count );
exe.shutdown();
}
ExecutorCompletionService類將Executor與BlockQueue結合在一起,每個線程完成後,會將其返回值放在阻塞隊列中;通過使用阻塞隊列的take或poll方法,就可以獲得結果。當阻塞隊列中不存在元素時,這兩個操作會阻塞,一旦有結果加入,一有結果就會立即返回。