最简单、最熟悉的方案,实现该接口需要重写run方法,缺点是没有返回值
Runnable runnable = () -> System.out.println("runnable 方法");
new Thread(runnable).start();
该接口中有一个V call方法,可以返回泛型值V并抛出异常
//传递返回值
Callable<String> callable = () -> "Callable 方法";
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(callable);
System.out.println(future.get());
executorService.shutdown();
//抛出异常,该操作在runnable中是不可行的
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
throw new InterruptedException("sss");
}
};
在上面Callable的例子中,我们可以看到使用Future去接callable执行的结果,
然后使用future.get()得到返回值
Future的本义是“未来”,表明我们在程序中需要使用callable计算结果时,可以随时调用future.get()进行获取,
如果计算完成了就直接返回结果,没有计算完就进行阻塞,直到计算完成。
这样可以让我们的主线程与其他线程分离,我们只需关注计算的结果。
Future接口包含以下方法:
get():获取结果(可能会等待)
get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
cancel(boolean mayInterruptIfRunning):取消当前任务;
isDone():判断任务是否已完成。
Future看起来比较完美了,但是仍然存在一些问题:
使用Future获取异步执行结果时候,要么调用get()阻塞等待,要么使用isDone()不停轮询,这两种方法会造成主线程的被迫等待。
于是java 8中引入了CompletableFuture,对Future进行改进,可以传入一个回调对象,当异步任务完成或者发生异常时,
可以调用回调方法。
下面的例子说明Future会阻塞主线程
Long startTime = System.currentTimeMillis();
System.out.println("主线程开始运行");
Callable<String> callable = () -> {Thread.sleep(2000); return "我是结果";};
ExecutorService executorService = Executors.newSingleThreadExecutor();
System.out.println("=====异步线程开始运行=====");
Future<String> future = executorService.submit(callable);
System.out.println(future.get());
executorService.shutdown();
System.out.println("=====异步线程执行结束=====");
Long endTime = System.currentTimeMillis();
System.out.println("主线程结束,耗时为"+ (endTime - startTime) + "ms");
结果是:
主线程开始运行
=====异步线程开始运行=====
我是结果
=====异步线程执行结束=====
主线程结束,耗时为2041ms
如果改成CompletableFuture:
主线程不必卡在get()上等待,可以去做其他事情,最后获取异步计算的执行结果
Long startTime = System.currentTimeMillis();
System.out.println("主线程开始运行");
Supplier<String> supplier = () -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "我是结果";};
System.out.println("=====异步线程开始运行=====");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(supplier);
completableFuture.thenAccept(System.out::println);
System.out.println("=====异步线程执行结束=====");
Long endTime = System.currentTimeMillis();
//主线程做其他事情
Thread.sleep(3000);
System.out.println("主线程结束,耗时为"+ (endTime - startTime) + "ms");
输出结果为:
主线程开始运行
=====异步线程开始运行=====
=====异步线程执行结束=====
我是结果
主线程结束,耗时为38ms
CompletableFuture的最大优势是链式调用,多个CompletableFuture可以串行执行。在异步完成A任务后,使用A的结果
继续完成B任务,最后再把结果返回主线程。
以下面的代码为例:
功能是异步生成1~5之间的随机数后加10。completableFuture1负责生产随机数,completableFuture2负责将随机数加10。
两个任务互相衔接,最后输出结果。
Supplier<Integer> supplier = () -> ThreadLocalRandom.current().nextInt(1, 6);
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(supplier);
CompletableFuture<Integer> completableFuture2 = completableFuture1.thenApplyAsync((result)-> result+10 );
completableFuture2.thenAccept(System.out::println);
多个CompletableFuture也可以并行执行。假如任务C需要任务A、B中的任意一个结果,那么就可以比较A和B哪个最先完成,然后立即传递给C。
以下面的代码为例:
两个线程分别生成苹果和香蕉,而小猴子最后吃到的是苹果。
Supplier<String> apple = () -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "苹果";
};
Supplier<String> banana = () -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "香蕉";};
CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(apple);
CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(banana);
CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(completableFuture1,completableFuture2);
completableFuture.thenApplyAsync((result)-> "小猴子饿了,要吃:" + result).thenAccept(System.out::println);
Thread.sleep(3000);
结果为:小猴子饿了,要吃:苹果
CompletableFuture异步调用多线程的方法:
上面我们大量使用了带有返回值的.supplyAsync发起异步调用,CompletableFuture中还有这些方法:
supplyAsync(Supplier supplier)
参数为Supplier类型的对象,返回CompletableFuture类型的对象。
其中Supplier是一种函数式接口,没有任何参数,只是返回一个结果() -> {xxxxx};
supplyAsync(Supplier supplier,Executor executor)
增加了Executor参数,可以使用线程池中的线程进行计算
runAsync(Runnable runnable)
runAsync 将 Runnable 作为输入参数并返回 CompletableFuture,这意味着它不返回任何结果。
runAsync(Runnable runnable,Executor executor)
增加了Executor参数,可以使用线程池中的线程进行计算
CompletableFuture可以指定异步处理流程:
thenAccept()处理正常结果;
exceptional()处理异常结果;
thenApplyAsync()用于串行化另一个CompletableFuture;
anyOf()和allOf()用于并行化多个CompletableFuture。
参考文献:
1 https://blog.hackajob.co/concurrency-and-improvements-in-java-8-part-1/
2 http://moguhu.com/article/detail?articleId=43
3 https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650
4 https://stackoverflow.com/questions/60159153/completablefuture-runasync-vs-supplyasync-when-to-choose-one-over-the-other