搞过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);
}
}
复制代码