在一些简单的业务场景使用Future创建异步任务是完全足够的。当业务场景比较复杂时,推荐使用CompletableFuture。
CompletableFuture同时实现了Future、CompletionStage两个接口。CompletionStage代表异步计算过程的某一个阶段,一个阶段完成后则会触发另一个阶段。
1.获取异步任务的结果
CompletableFuture可通过回调方法在异步任务完成后回调whenComplete()方法主动进行下一步操作,而Future则只能通过阻塞或轮询的方式获取异步返回值(调用get()方法会导致线程阻塞与异步思想相悖、轮询则会消耗无谓的CPU资源)。
2.组合多个异步任务
在当前异步任务需要调用上一个异步任务的结果时,CompletableFuture可将这多个异步任务组合成一个任务,而Future则只能创建多个任务并以阻塞的方式获取上一个异步任务的结果供下一个任务调用。
3.返回最快的处理结果
当Future集合中某个任务结束最快时,返回最快的处理结果。
1.CompletableFuture的四种构建方式
//创建没有返回值的CompletableFuture
public static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable, Executor executor)
//创建有返回值的CompletableFuture
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
在不指定线程池的方法中,直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码。
2.CompletableFuture的基本使用
使用无返回值的方式构建一个CompletableFuture。
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadPool);
System.out.println(completableFuture.get());
threadPool.shutdown();
}
}
使用带返回值的方式构建一个CompletableFuture。
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello CompletableFuture";
});
System.out.println(completableFuture.get());
threadPool.shutdown();
}
}
使用CompletableFuture的回调函数实现任务的集成。由于CompletableFuture类似于一个守护线程,所以在主线程完成后守护线程会自动停止,这种情况会因为主线程执行结束导致CompletableFuture线程执行到一半就停止。因此建议使用线程池构建CompletableFuture线程。
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
int a = 10;
int b = 2;
return a / b;
}, threadPool).whenComplete((v,e) -> {//当上一步操作完成后调用该方法。v表示上一步的结果,e表示异常。
int c = 5;
System.out.println(v + c);//10
}).exceptionally(e -> {//出现异常时调用该方法
e.printStackTrace();
return 0;
});
System.out.println("主线程任务");
threadPool.shutdown();
}
}
3.CompletableFuture的其它方法
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello CompletableFuture";
});
//在结果还未计算出时,使用缺省值
System.out.println(completableFuture.getNow("please waiting"));
//运行后会输出please waiting
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello CompletableFuture";
});
//在此处调用get()方法阻塞,complete()方法会打断get返回true
System.out.println(completableFuture.complete("return value"));
//调用get()方法会直接返回return value
System.out.println(completableFuture.get());
//运行结果
// true return value
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始处理");
return 1;
}, threadPool).thenApply(f -> {
System.out.println("add 1");
return f + 1;
}).thenApply(f -> {
System.out.println("add 2");
return f + 2;
}).whenCompleteAsync((v,e) -> {
System.out.println("输出结果 --> " + v);
}).exceptionally(e -> {
System.out.println("出现异常");
return 0;
});
System.out.println("主线程任务");
threadPool.shutdown();
}
}
//输出
主线程任务
开始处理
add 1
add 2
输出结果 --> 4
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("开始处理");
return 1;
}, threadPool).handle((f,e) -> {
System.out.println("add 1");
int i = 10 / 0;
return f + 1;
}).handle((f,e) -> {
System.out.println("add 2");
return f + 2;
}).whenCompleteAsync((v,e) -> {
System.out.println("输出结果 --> " + v);
}).exceptionally(e -> {
System.out.println("出现异常");
return 0;
});
threadPool.shutdown();
}
}
//输出
开始处理
add 1
add 2
输出结果 --> null
出现异常
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
System.out.println("开始处理");
return 1;
}, threadPool).thenAccept((c) -> {
System.out.println("输出结果 --> " + c);
});
threadPool.shutdown();
}
}
//输出
开始处理
输出结果 --> 1
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
System.out.println("A coming");
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace();}
return "A";
});
CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {
System.out.println("B coming");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace();}
return "B";
});
CompletableFuture future = futureA.applyToEither(futureB, f -> {
System.out.println(f + " is winner");
return f + " is winner";
});
threadPool.shutdown();
}
}
//输出
A coming
B coming
B is winner
public class CompletableFutureStageDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
System.out.println("流程1");
return 10;
});
CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {
System.out.println("流程2");
return 10;
});
CompletableFuture future = futureA.thenCombine(futureB, (x, y) -> {
System.out.println("开始合并");
return x + y;
});
System.out.println(future.join());
threadPool.shutdown();
}
}
//结果
流程1
流程2
开始合并
20
知识点:调用thenRun()与thenRunAsync()时的线程池区别,调用thenRun()方法时会与supplyAsync共用同一个线程池,调用thenRunAsync()方法时则会判断cpu内核数是否大于1,若大于1则会使用默认的ForkJoinPool线程池。