在开始学习completablefuture,发现他是Future的升级,所以先来了解一下Future
一 Future–interface
在并发编程中,我们经常用到非阻塞的模型,在之前的多线程的三种实现中,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。
Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。
举个例子:比如去吃早点时,点了包子和凉菜,包子需要等3分钟,凉菜只需1分钟,如果是串行的一个执行,在吃上早点的时候需要等待4分钟,但是因为你在等包子的时候,可以同时准备凉菜,所以在准备凉菜的过程中,可以同时准备包子,这样只需要等待3分钟。那Future这种模式就是后面这种执行模式。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning); //还没计算完,可以取消计算过程
boolean isCancelled(); //判断计算是否被取消
boolean isDone(); //判断是否计算完
V get() throws InterruptedException, ExecutionException; //获取计算结果(如果还没计算完,也是必须等待的)
V get(long timeout, TimeUnit unit) //最多等待timeout的时间就会返回结果
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask是Future接口的一个唯一实现类。 所以在使用Future时候是使用FutureTask+Callable,因为callable有返回值。
二 Future的局限性
Future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:
- 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。
- 等待 Future 集合中的所有任务都完成。
- 仅等待 Future集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同一个值),并返回它的结果。
- 通过编程方式完成一个Future任务的执行(即以手工设定异步操作结果的方式)。
- 应对 Future 的完成事件(即当 Future的完成事件发生时会收到通知,并能使用 Future 计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)
新的CompletableFuture类将使得这些成为可能。
三 CompletableFuture
一些使用经验:
1,自定义线程池比list小的时候,线程池全部被占用,线程池队列占满,其他的任务也没有被丢弃,至于怎么实现的还没搞清楚,有理解的朋友 欢迎留言或私信告知。
a 创建任务
CompletableFuture源码中有四个静态方法用来执行异步任务:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}
public static CompletableFuture<Void> runAsync(Runnable runnable){..}
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor){..}
上述方法前两个都是返回一个新的CompletableFuture,如果给定线程池则用给定的线程池,如果不给的话使用默认的ForkJoinPool.commonPool(),有人说自己给定效果好,没验证,不过自己给定好对线程池进行控制。
前面两个是有返回值的,后面两个是没有返回值的。
执行异步任务的方式很简单
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//....执行任务
return "hello";}, executor)
b 获取执行结果的方法:
public boolean isDone() // true如果以任何方式完成或通过取消。
public T get() throws InterruptedException,ExecutionException //阻塞等待,然后返回结果。
public T get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException //阻塞等待结果,超时抛异常
public T join() //完成后返回结果值,如果完成异常,则返回(未检查)异常。 为了更好地符合常用功能形式的使用,如果完成此CompletableFuture涉及的计算抛出异常,则该方法将引发(未选中) CompletionException ,其中的基础异常是其原因。
public T getNow(T valueIfAbsent) //如果已完成,则返回结果值(或抛出任何遇到的异常),否则返回给定的值IfAbsent。
get和join的区别:
两者都是等待结果,但是get会让你显式的捕获异常,而join不强制捕获,但如果代码异常也会抛出。另外join不能被打断(can not get interrupted)。
public boolean complete(T value)
public boolean completeExceptionally(Throwable ex)
future.get()在等待执行结果时,程序会一直block,如果此时调用complete(T t)会立即执行。但是complete(T t)只能调用一次,后续的重复调用会失效。如果future已经执行完毕能够返回结果,此时再调用complete(T t)则会无效。
如果使用completeExceptionally(Throwable ex)则抛出一个异常,而不是一个成功的结果。(引用2)
下面是我的测试代码
@Test
public void test2() {
System.out.println(System.currentTimeMillis());
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "mission A";
}
});
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "mission B";
});
CompletableFuture<String> futureC = futureB.thenApplyAsync(b -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("canshu" + b + System.currentTimeMillis());
System.out.println("mission C action" + System.currentTimeMillis());
return "mission C";
});
//you can change the code below to test the results
try {
if (!futureA.isDone()) {
System.out.println(futureA.getNow("can't wait anymore"));
}
String join = futureA.join();
try {
futureA.get((long) 0.5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
}
System.out.println(join);
System.out.println(futureA.get() + System.currentTimeMillis());
System.out.println(futureB.get() + System.currentTimeMillis());
System.out.println(futureC.get() + System.currentTimeMillis());
if (futureA.isDone()) {
System.out.println("mission A is done");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
c CompletionStage
CompletionStage 代表异步计算中的一个阶段或步骤。
该接口定义了多种不同的方式,将CompletionStage 实例与其他实例或代码链接在一起,比如完成时调用的方法(一共 59 种方法,比Future 接口中的 5 种方法要多得多。)
d 其他方法 (引用1介绍的不错,比较好)
可以自己写一些小栗子加深理解
@Test
public void test4() {
System.out.println(System.currentTimeMillis());
List<CreateOrderCarInfo> callList = Lists.newArrayList(new CreateOrderCarInfo("A"), new CreateOrderCarInfo("B"), new CreateOrderCarInfo("C"));
List<CompletableFuture<String>> collect = callList.stream().map(carInfo -> CompletableFuture.supplyAsync(() -> carInfo.createChildOrder("public"))).collect(Collectors.toList());
List<String> res = collect.stream().map(CompletableFuture::join).collect(Collectors.toList());
for (String re : res) {
if ("2".equals(re)) {
System.out.println("didi" + System.currentTimeMillis());
return;
}
if ("1".equals(re)) {
System.out.println("fail" + System.currentTimeMillis());
return;
}
}
System.out.println("success" + System.currentTimeMillis());
}
public class CreateOrderCarInfo {
private String privates;
public CreateOrderCarInfo(String privates) {
this.privates = privates;
}
public String createChildOrder(String carOrder) {
double v = 1000 * Math.random() + 1000;
int i = (int)(Math.random() * 3);
try {
Thread.sleep((long) v);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("new " + privates + System.currentTimeMillis());
return privates + String.valueOf(i);
}
}
注:引用
1.https://blog.csdn.net/qq_42606051/article/details/84028376
2.https://blog.csdn.net/u012129558/article/details/78962759