最近项目里有使用到FutureTask这一组件,顺便写个简要总结。
1. 定义:
Future,FutureTask和CompletableFuture都代表了异步计算的结果。
2. Future和FutureTask和CompletableFuture的关系
如图所示,Future和FutureTask都能异步的获取线程执行结果,但是FutureTask不仅实现Future接口,并且实现了Runable接口,所以我们可以直接将FutureTask提交到线程池执行,同时也可以获取执行结果,但是Future仅支持获取Callable的执行结果,所以可见FutureTask功能更加全面且方便。
FutureTask底层是基于AQS实现的,具体原理可以看源码。
CompletableFuture是jdk1.8以后出现的,和前面的Future以及FutureTask有很大区别,功能更加强大且复杂,而且融入了大量的流式编程思想,详细介绍可以看这篇博客。
3. 应用场景
Java中线程的实现方式一般有实现Runable接口,实现Callable接口,继承Thread类三种。而Runable和Thread的run方法都是void,也就是没有返回值,所以这种情况下获取线程的返回值就要使用线程间通信的手段,一般比较麻烦;或者使用Callable接口,但是Callable接口本身只能支持同步的结果获取,所以Future和FutureTask就应时而生了。
Future和FutureTask提供一种轻量级的获取异步任务结果的手段。
而CompletableFuture则是提供了更加丰富的api,来进行异步的计算,转化,消费,多个CompletableFuture合并,返回结果等,相对来说比较重量级。
4. Demo:
简单实现了两个Demo,模拟任务提交,分别实现了基于Future和FutureTask的结果获取,以及同步和异步两种方式,仔细理解体会,下次就能想到使用这一基础并发组件。
FutureCallableDemo
public class Job implements Callable {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
System.out.println("这是子线程");
return 1;
}
}
public class FutureCallableDemo {
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newSingleThreadExecutor();
Callable callable = new Job();
/**
* 同步方式
*/
//System.out.println(callable.call());
/**
* 异步方式
*/
Future future = es.submit(callable);
System.out.println("主线程可以做自己的事情");
System.out.println(future.get());
es.shutdown();
}
}
FutureTaskDemo
public class Job implements Callable {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
System.out.println("这是子线程");
return 1;
}
}
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newSingleThreadExecutor();
FutureTask futureTask = new FutureTask<>(new Job());
/**
* 同步方式
*/
//futureTask.run();
/**
* 异步方式
*/
es.submit(futureTask);
System.out.println("主线程可以做自己的事情");
System.out.println(futureTask.get());
es.shutdown();
}
}
CompletableFutureDemo
public class CompletableFutureDemo {
private static ExecutorService es = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
/**
* 最基本的Future功能演示
*/
System.out.println(CompletableFuture.supplyAsync(() -> "mysql IO", es)
.get());
System.out.println("--------------------------------------------------------------------------");
/**
* combine演示
*/
CompletableFuture.supplyAsync( () -> 123, es)
.thenCombine(CompletableFuture.supplyAsync( () -> 456, es), (x, y) -> x + " " + y)
.whenComplete((s, v) -> System.out.println("合并结果: " + s));
System.out.println("--------------------------------------------------------------------------");
/**
* 创建CompletableFuture对象计算,转化结果,计算完成后进行消费,并返回结果(或者进行纯消费)演示
*/
List taskList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
CompletableFuture[] cfs = taskList.stream() // stream
.map(o -> CompletableFuture.supplyAsync(() -> cal(o), es) // 创建CompletableFuture对象
.thenApply(h -> Integer.toString(h)) // 结果转化
.whenComplete((v, e) -> System.out.println("任务" + v + "完成!result=" + v + ",异常 e=" + e + "," + new Date())) // 计算完成时消费
)
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join(); // 等待所有的CompletableFuture执行完成,然后返回结果
}
private static Integer cal(Integer i) {
try {
if (i == 1) {
Thread.sleep(3000);//任务1耗时3秒
} else if (i == 5) {
Thread.sleep(5000);//任务5耗时5秒
} else {
Thread.sleep(1000);//其它任务耗时1秒
}
System.out.println("task线程:" + Thread.currentThread().getName() + "任务i=" + i + ",完成!+" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
return i;
}
}
Github:项目地址
参考文献:
《Java编程思想》
《Java并发编程的艺术》