搞過Java或者客戶端Android開發的都知道,建立線程的2種方式,一種是直接繼承Thread,另一種就是實現Runnable接口。不過,這2種方式都有一個缺陷,就是在執行完任務以後沒法獲取執行結果。java
若是須要獲取執行結果,就必須經過共享變量或者使用線程通訊的方式來達到效果,這就涉及到線程切換和線程通訊等問題,就比較的麻煩。 不過,好在Java 從1.5版本開始,就提供了Callable和Future,經過它們能夠在任務執行完畢以後獲得任務執行結果。bash
先看一下java.lang.Runnable,Runnable是一個接口,它裏面只聲明瞭一個run()方法。代碼以下:多線程
public interface Runnable {
public abstract void run();
}
複製代碼
因爲run()方法是一個void類型的,因此在執行完任務以後沒法返回任何結果。異步
Callable位於java.util.concurrent包下,它也是一個接口,它裏面也只聲明瞭一個方法,只不過這個方法叫作call()。代碼以下:ide
public interface Callable<V> {
V call() throws Exception;
}
複製代碼
能夠發現,Callable接受一個泛型,call()函數返回的類型就是傳遞進來的V類型。 那麼怎麼使用Callable呢?通常狀況下Callable須要和ExecutorService配合使用,在ExecutorService接口中聲明瞭若干個submit方法的重載版本。函數
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
複製代碼
其中,第一個submit方法裏面的參數類型就是Callable。如下是一個完整的使用示例:ui
package thread.learn;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableAndFuture {
static class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello world";
}
}
static class MyThread2 implements Runnable {
@Override
public void run() {
}
}
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new MyThread());
try {
System.out.println(future.get());
} catch (Exception e) {
} finally {
threadPool.shutdown();
}
}
}
複製代碼
Future表示一個可能尚未完成的異步任務的結果,針對這個結果能夠添加Callback以便在任務執行成功或失敗後做出相應的操做。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。spa
Future類位於java.util.concurrent包下,它也是一個接口,定義以下:線程
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
複製代碼
能夠發現,Future接口中聲明瞭5個方法,具體的含義以下:code
綜上能夠發現,Future具備3種能力: 1 )判斷任務是否完成; 2)可以中斷任務; 3)可以獲取任務執行結果。
同時,Future有4個子類,類圖結構以下:
ScheduledFuture ScheduledFuture接口表示一個延時的行爲能夠被取消,一般須要將一個安排好的Future配合定時任務SchedualedExecutorService執行,並返回執行的結果。
CompleteFuture Complete表示操做已完成,因此CompleteFuture表示一個異步操做已完成的結果。當兩個或多個線程要執行完成或取消操做時,只有一個可以成功。
ForkJoinPool ForkJoinPool是一個基於任務的抽象類,能夠經過ForkJoinPool來執行。一個ForkJoinTask是相似於線程實體,可是相對於線程實體是輕量級的。大量的任務和子任務會被ForkJoinPool池中的真實線程掛起來,以某些使用限制爲代價。
先來看一下FutureTask的實現,源碼以下:
public class FutureTask<V> implements RunnableFuture<V>
複製代碼
FutureTask類實現了RunnableFuture接口,再看一下RunnableFuture接口的實現。源碼以下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
複製代碼
能夠看出,RunnableFuture繼承了Runnable接口和Future接口,而FutureTask實現了RunnableFuture接口。因此它既能夠做爲Runnable被線程執行,又能夠做爲Future獲得Callable的返回值。
Callable+Future獲取多線程的執行結果。
public class FutureDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主線程在執行任務");
try {
System.out.println("task運行結果"+result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("全部任務執行完畢");
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子線程在進行計算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++){
sum += i;
}
return sum;
}
}
複製代碼
執行結果:
子線程在進行計算
主線程在執行任務
task運行結果4950
全部任務執行完畢
複製代碼
使用Callable+FutureTask獲取多線程的執行結果。
public class FutureTask {
public static void main(String[] args) {
//第一種方式
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
executor.submit(futureTask);
executor.shutdown();
//第二種方式,注意這種方式和第一種方式效果是相似的,只不過一個使用的是ExecutorService,一個使用的是Thread
/*Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask);
thread.start();*/
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主線程在執行任務");
try {
System.out.println("task運行結果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("全部任務執行完畢");
}
}
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子線程在進行計算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++){
sum += i;
}
return sum;
}
}
複製代碼
例如,下面是一個典型的多線程場景:好比去吃早點時,點了包子和涼菜,包子須要等3分鐘,涼菜只需1分鐘,若是是串行的一個執行,在吃上早點的時候須要等待4分鐘,可是由於你在等包子的時候,能夠同時準備涼菜,因此在準備涼菜的過程當中,能夠同時準備包子,這樣只須要等待3分鐘。
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
// 等涼菜
Callable ca1=new Callable() {
@Override
public Object call() throws Exception {
try {
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
return "等涼菜";
}
};
FutureTask<String> ft1 = new FutureTask<String>(ca1);
new Thread(ft1).start();
//等包子
Callable ca2=new Callable() {
@Override
public Object call() throws Exception {
try {
Thread.sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
return "等包子";
}
};
FutureTask<String> ft2 = new FutureTask<String>(ca2);
new Thread(ft2).start();
System.out.println(ft1.get());
System.out.println(ft2.get());
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
複製代碼