Java从JDK5开始引入了Future,用来描述一个异步计算的结果,平时开发过程中 Runable
、Future
、 Thread
、ExecutorService
、Callable
这些和多线程相关的类相互配合对开发人员来说已经非常熟悉和了解。使用起来也得心应手。但是这些类组合在一起解决多线程的问题的同时也逐渐暴露出难以满足的开发需求。
在JDK8之前,我们使用的Java多线程编程,主要是 Thread+Runnable 来完成,但是这种方式有个最大的弊端就是没有返回值。如果想要返回值怎么办呢,大多数人就会想到 Future+Callable
的方式来获取到返回值。Future是一个可以代表异步计算结果的对象,并且Future提供了一些方法来让调用者控制任务,比如可以取消任务的执行(当然可能取消会失败),或者设置超时时间来取得我们的任务的运行结果。如下:
FutureTask<String> task = new FutureTask((Callable<String>) ()->{
TimeUnit.SECONDS.sleep(2);
return UUID.randomUUID().toString();
});
new Thread(task).start();
String s = task.get();
System.out.println(s);
虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。
从上面的示例来看轮询的方式势必会耗费无谓的CPU资源,而且也不能及时地得到计算结果.所以要实现真正的异步,上述这样是完全不够的。
Runnable+Thread
虽然提供了多线程的能力但是没有返回值。Future + Callable
的方法提供多线程和返回值的能力但是在获取返回值的时候会阻塞主线程。 所以,从JDK1.8开始,CompletableFuture
就为我们提供了异步函数式编程,CompletableFuture
提供了非常强大的Future
的扩展功能,CompletableFuture
继承了FutureTask
的同步任务的特点,同时新增了异步调用的特点,说简单点就是它可以让代码一起运行,不需要一个个运行,CompletableFuture
可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture
的方法。
public class CompletableFuture<T>
extends Object
implements java.util.concurrent.Future<T>, java.util.concurrent.CompletionStage<T>
CompletableFuture
实现了Future
和CompletionStage
两个接口。
其中Future大家应该都很熟悉了,在异步应用中也很常见,这里简单的回顾下普通模式和Future模式的区别:
可以看到当工作线程的结果我们并不急着需要的话,可以交给Future,然后主线程可以去做一些别的事情,当需要工作线程结果的时候,使用get()来尝试获取即可。注意get()方法是阻塞的,这也是Future常被吐槽的地方,另外Future无法表达任务间的依赖关系也是它的一个局限。
CompletableFuture
实现了Future
的所有接口,包括两个get方法,一个是不带参数的get方法,一个是可以设置等待时间的get方法,首先来看一下CompletableFuture
中不带参数的get方法的具体实现:
public T get() throws InterruptedException, ExecutionException {
Object r;
return reportGet((r = result) == null ? waitingGet(true) : r);
}
result字段代表任务的执行结果,所以首先判断是否为null,为null则表示任务还没有执行结束,那么就会调用waitingGet
方法来等待任务执行完成,如果result不为null,那么说明任务已经成功执行结束了,那么就调用reportGet
来返回结果,下面先来看一下waitingGet
方法的具体实现细节:
/**
* Returns raw result after waiting, or null if interruptible and
* interrupted.
*/
private Object waitingGet(boolean interruptible) {
Signaller q = null;
boolean queued = false;
int spins = -1;
Object r;
while ((r = result) == null) {
if (spins < 0)
spins = (Runtime.getRuntime().availableProcessors() > 1) ?
1 << 8 : 0; // Use brief spin-wait on multiprocessors
else if (spins > 0) {
if (ThreadLocalRandom.nextSecondarySeed() >= 0)
--spins;
}
else if (q == null)
q = new Signaller(interruptible, 0L, 0L);
else if (!queued)
queued = tryPushStack(q);
else if (interruptible && q.interruptControl < 0) {
q.thread = null;
cleanStack();
return null;
}
else if (q.thread != null && result == null) {
try {
ForkJoinPool.managedBlock(q);
} catch (InterruptedException ie) {
q.interruptControl = -1;
}
}
}
if (q != null) {
q.thread = null;
if (q.interruptControl < 0) {
if (interruptible)
r = null; // report interruption
else
Thread.currentThread().interrupt();
}
}
postComplete();
return r;
}
这个方法的实现时比较复杂的,方法中有几个地方需要特别注意,下面先来看一下spins是做什么的,根据注释,可以知道spins是用来在多核心环境下的自旋操作的,所谓自旋就是不断循环等待判断,从代码可以看出在多核心环境下,spins会被初始化为1 << 8,然后在自旋的过程中如果发现spins大于0,那么就通过一个关键方法ThreadLocalRandom.nextSecondarySeed()
来进行spins的更新操作,如果ThreadLocalRandom.nextSecondarySeed()
返回的结果大于0,那么spins就减1,否则不更新spins。 ThreadLocalRandom.nextSecondarySeed()
方法其实是一个类似于并发环境下的random,是线程安全的。
接下来还需要注意的一个点是Signaller
,从Signaller
的实现上可以发现,Signaller
实现了ForkJoinPool.ManagedBlocker
,下面是ForkJoinPool.ManagedBlocker
的接口定义:
public static interface ManagedBlocker {
/**
* Possibly blocks the current thread, for example waiting for
* a lock or condition.
*
* @return {@code true} if no additional blocking is necessary
* (i.e., if isReleasable would return true)
* @throws InterruptedException if interrupted while waiting
* (the method is not required to do so, but is allowed to)
*/
boolean block() throws InterruptedException;
/**
* Returns {@code true} if blocking is unnecessary.
* @return {@code true} if blocking is unnecessary
*/
boolean isReleasable();
}
ForkJoinPool.ManagedBlocker
的目的是为了保证ForkJoinPool
的并行性,具体分析还需要更为深入的学习Fork/Join
框架。继续回到waitingGet
方法中,在自旋过程中会调用ForkJoinPool.managedBlock
(ForkJoinPool.ManagedBlocker
)来进行阻塞工作,实际的效果就是让线程等任务执行完成,CompletableFuture
中与Fork/Join
的交叉部分内容不再本文的描述范围,日后再进行分析总结。总得看起来,waitingGet
实现的功能就是等待任务执行完成,执行完成返回结果并做一些收尾工作。
现在来看reportGet
方法的实现细节,在判断任务执行完成之后,get方法会调用reportGet
方法来获取结果:
/**
* Reports result using Future.get conventions.
*/
private static <T> T reportGet(Object r)
throws InterruptedException, ExecutionException {
if (r == null) // by convention below, null means interrupted
throw new InterruptedException();
if (r instanceof AltResult) {
Throwable x, cause;
if ((x = ((AltResult)r).ex) == null)
return null;
if (x instanceof CancellationException)
throw (CancellationException)x;
if ((x instanceof CompletionException) &&
(cause = x.getCause()) != null)
x = cause;
throw new ExecutionException(x);
}
@SuppressWarnings("unchecked") T t = (T) r;
return t;
}
分析完了不带参数的get方法(阻塞等待)之后,现在来分析一下带超时参数的get方法的具体实现细节:
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
Object r;
long nanos = unit.toNanos(timeout);
return reportGet((r = result) == null ? timedGet(nanos) : r);
}
和不带参数的get方法一样,还是会判断任务是否已经执行完成了,如果完成了会调用reportGet
方法来返回最终的执行结果(或者抛出异常),否则,会调用timedGet
来进行超时等待,timedGet
会等待一段时间,然后抛出超时异常(或者执行结束返回正常结果),下面是timedGet
方法的具体细节:
private Object timedGet(long nanos) throws TimeoutException {
if (Thread.interrupted())
return null;
if (nanos <= 0L)
throw new TimeoutException();
long d = System.nanoTime() + nanos;
Signaller q = new Signaller(true, nanos, d == 0L ? 1L : d); // avoid 0
boolean queued = false;
Object r;
// We intentionally don't spin here (as waitingGet does) because
// the call to nanoTime() above acts much like a spin.
while ((r = result) == null) {
if (!queued)
queued = tryPushStack(q);
else if (q.interruptControl < 0 || q.nanos <= 0L) {
q.thread = null;
cleanStack();
if (q.interruptControl < 0)
return null;
throw new TimeoutException();
}
else if (q.thread != null && result == null) {
try {
ForkJoinPool.managedBlock(q);
} catch (InterruptedException ie) {
q.interruptControl = -1;
}
}
}
if (q.interruptControl < 0)
r = null;
q.thread = null;
postComplete();
return r;
}
在timedGet
中不再使用spins来进行自旋,因为现在可以确定需要等待多少时间了。timedGet
的逻辑和waitingGet
的逻辑类似,毕竟都是在等待任务的执行结果。
除了两个get方法之前,CompletableFuture
还提供了一个方法getNow
,代表需要立刻返回不进行阻塞等待,下面是getNow
的实现细节:
public T getNow(T valueIfAbsent) {
Object r;
return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
}
getNow很简单,判断result是否为null,如果不为null则直接返回,否则返回参数中传递的默认值。
CompletableStage
用来表示异步过程中的一个阶段,它可以在另一个CompletableStage
完成时做一些操作或计算,此接口中定义了一些基本的行为,通过这些行为组合可以简洁的描述非常复杂的任务。
常用的几个方法:
thenApply
将上一个stage的结果转化成新的类型或值thenAccept
将上一个stage的结果进行消耗,无返回值thenRun
在上一个stage有结果后,执行一段新的操作thenCombine
结合两个CompletableStage
的结果,转化成新的类型或值thenCompose
返回一个新的CompletableStage
,并将上一个stage的结果作为新的stage的supplierexceptionally
当运算过程中遇到异常时的一个补偿处理handle
统一了对正常结果和异常结果的处理大部分方法都有以Async
结尾的方法,表示异步执行,后面会提到。更多信息可以参考jdk文档。
CompletableFuture
初始化时可以处于completed和incompleted两种状态,先看两个最简单的例子。
// base直接初始化成一个已完成的CompletableFuture,完成值是"completed"
CompletableFuture<String> base = CompletableFuture.completedFuture("completed");
log.info(base.get());
输出:
[INFO ] [2019-07-15 10:05:13] [main] completed
这里base对象是一个已完成的CompletableFuture
,所以get()直接返回了"completed"。当然如果初始化时用了未完成的CompletableFuture
,那么get()方法是会阻塞等待它完成,这个就成了Future模式,毕竟get()方法是在Future
接口中定义的。
我们也可以在之后的代码中,或是其他线程中将它“完成”:
// 这是一个未完成的CompletableFuture
CompletableFuture<String> base = new CompletableFuture<>();
log.info("start another thread to complete it");
new Thread(
() -> {
log.info("will complete in 1 sec");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
base.complete("completed");
})
.start();
log.info(base.get());
输出:
[INFO ] [2019-07-15 14:32:26] [main] start another thread to complete it
[INFO ] [2019-07-15 14:32:26] [Thread-0] will complete in 1 sec
[INFO ] [2019-07-15 14:32:27] [main] completed
这个例子中主线程在调用get()方法时阻塞,Thread-0线程在sleep 1秒后调用complete()方法将base完成,主线程get()返回得到完成值completed。
CompletableFuture<String> base = new CompletableFuture<>();
base.completeExceptionally(new RuntimeException(("runtime error")));
log.info(base.get());
输出:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException: runtime error
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at com.aliyun.completable.Main.main(Main.java:33)
Caused by: java.lang.RuntimeException: runtime error
at com.aliyun.completable.Main.main(Main.java:32)
在complete时发生异常,在base调用get()方法时抛出ExecutionException
。
我们可以得出最基本的一个流程,CompletableFuture
是靠complete作为一个初始力来驱动的,虽然这不是它的全部,但至少得complete它才会去继续执行后面依赖它的一系列处理。
CompletableFuture两个核心的关键属性result和stack,关于这两个属性也有核心的注释,result可能是返回的结果集,也可能是包装的AltResult,stack这个数据暴露出了CompletableFuture的整体的结构是一个栈。
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
Completion的源码:
abstract static class Completion extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
volatile Completion next;
abstract CompletableFuture<?> tryFire(int mode);
abstract boolean isLive();
public final void run() { tryFire(ASYNC); }
public final boolean exec() { tryFire(ASYNC); return true; }
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
}
Completion
是一个抽象类,分别实现了Runnable
、AsynchronousCompletionTask
接口,继承了ForkJoinPoolTask
类,而ForJoinPoolTask
抽象类又实现了Future
接口,因此Completion
实际上就是一个Future
。可以看到Completion
的抽象方法和成员方法的实现逻辑都短短一行或者没有,可以猜到这些方法的实现都是在其子类中。其实现类包括了UniCompletion
、BiCompletion
、UniAccept
、BiAccept
等。
结合我们上面看到的CompletableFuture
的stack属性,整好能验证CompletableFuture
是一个链表的一个数据结构,Completion
中的next保存了栈中下一个元素的引用,而CompletableFuture
中的stack永远指向栈顶。
//runAsync 执行的任务无返回值,
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
//supplyAsync 执行的任务有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
//以上传入 Executor 可以使用我们指定的线程池,不传则使用默认的 ForkJoinPool.commonPool()线程池
CompletableFuture
源码:
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
// 默认线程池 —— ForkJoinPool.commonPool() 除非它不支持并行。
private static final Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
// 如果 ForkJoinPool.commonPool() 不支持并行性,则回退
static final class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
// 返回一个新的 CompletableFuture,它在运行给定操作后由 ForkJoinPool.commonPool() 中运行的任务异步完成。
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
// 返回一个新的 CompletableFuture,它由在 ForkJoinPool.commonPool() 中运行的任务异步完成,调用者可以获取返回值。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
}
测试 1:
@Slf4j(topic = "c.TestsSupplyAsync")
public class TestsSupplyAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(() -> {
log.debug("线程名:{}", Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello supplyAsync";
}, pool);
log.debug("{}", supplyAsync.get());
// 也可以使用join,使用join可以不用声明异常
// log.debug("{}", supplyAsync.join());
pool.shutdown();
}
}
17:52:23.694 c.TestsSupplyAsync [pool-1-thread-1] - 线程名:pool-1-thread-1
17:52:24.706 c.TestsSupplyAsync [main] - hello supplyAsync
测试 2:
需求:
同时搜索出同款产品在各大平台的售价
输出格式:List< String>
《Java》in JD price is ***
《Java》in DangDang price is ***
《Java》in Taobao price is ***
public class TestCompletableFuture {
static List<Mall> list = Arrays.asList(
new Mall("JD"),
new Mall("DangDang"),
new Mall("TaoBao")
);
public static void main(String[] args) {
long start = System.currentTimeMillis();
List<String> price = getPrice(list, "java");
price.forEach(System.out::println);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "毫秒");
}
// List -> List> -> List
public static List<String> getPrice(List<Mall> mallList, String bookName) {
return mallList.stream()
.map(mall -> CompletableFuture.supplyAsync(() ->
String.format(bookName + " in %s price is %.2f",
mall.getMallName(),
mall.getBookPrice(bookName))))
.collect(Collectors.toList())
.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
class Mall {
@Getter
private final String mallName;
public Mall(String mallName) {
this.mallName = mallName;
}
public double getBookPrice(String bookName){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 随机产生价格
return ThreadLocalRandom.current().nextDouble() * 2 + bookName.charAt(0);
}
}
java in JD price is 106.31
java in DangDang price is 106.35
java in TaoBao price is 107.81
耗时:1125毫秒
所有任务都执行完成后,才执行 allOf返回的CompletableFuture
。如果任意一个任务异常,allOf的CompletableFuture
,执行get方法,会抛出异常。
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{ System.out.println("第一个异步任务执行完了"); });
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> { System.out.println("第二个异步任务执行完了"); });
CompletableFuture.allOf(completableFuture,runAsync).whenComplete((a,b) -> {
System.out.println("finish");
});
任意一个任务执行完,就执行anyOf返回的CompletableFuture
。如果执行的任务异常,anyOf的CompletableFutur
e,执行get方法,会抛出异常。
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一个异步任务执行完了");
});
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
System.out.println("第二个异步任务执行完了");
});
CompletableFuture.anyOf(completableFuture,runAsync).whenComplete((a,b) -> {
System.out.println("finish");
});
获得结果类型的方法
// 等待任务完成,并返回结果
public T get() throws InterruptedException, ExecutionException
// 带超时的等待,并返回结果,超时则抛出异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
// 等待任务完成,并返回结果,不用声明异常 (编译时不会检查异常)
// 出现异常时,抛出 CompletionException 异常
public T join()
// 如果任务完成则返回结果值(或抛出任何遇到的异常),否则返回给定的参数值 valueIfAbsent
// 不会阻塞
public T getNow(T valueIfAbsent)
测试 getNow()
public static void main(String[] args) {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "测试getNow方法";
});
System.out.println("结果:" + completableFuture.getNow("这是任务没完成返回的指定值"));
结果:这是任务没完成返回的指定值
主动触发计算的方法
// 如果尚未完成,则将 get() 和相关方法返回的值设置为给定值
// 表示是否打断get()、join()等方法,立即返回指定值 value
public boolean complete(T value)
测试 complete():
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
// 执行 2s
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "测试getNow方法";
});
// 等 1s
TimeUnit.SECONDS.sleep(1);
// 是否打断get()等方法
boolean flag = completableFuture.complete("这是任务没完成返回的指定值");
String value = completableFuture.get();
System.out.println("是否打断:" + flag + ", 结果:" + value);
}
是否打断:true, 结果:这是任务没完成返回的指定值
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
测试正常情况:
@Slf4j(topic = "c.TestThenApply")
public class TestThenApply {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
log.debug("第一阶段");
return 1;
}, pool).thenApply((v) -> {
log.debug("第二阶段");
return v + 2;
}).thenApply((v) -> {
log.debug("第三阶段");
return v + 3;
}).whenComplete((v, e) -> {
log.debug("结果:" + v);
}).exceptionally(e -> {
log.debug("出现异常:" + e.getMessage());
return null;
});
pool.shutdown();
}
}
22:45:39.496 c.TestThenApply [pool-1-thread-1] - 第一阶段
22:45:39.499 c.TestThenApply [pool-1-thread-1] - 第二阶段
22:45:39.499 c.TestThenApply [pool-1-thread-1] - 第三阶段
22:45:39.500 c.TestThenApply [pool-1-thread-1] - 结果:6
测试异常情况:
@Slf4j(topic = "c.TestThenApply")
public class TestThenApply {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
log.debug("第一阶段");
return 1;
}, pool).thenApply((v) -> {
int i = 10 / 0;
log.debug("第二阶段");
return v + 2;
}).thenApply((v) -> {
log.debug("第三阶段");
return v + 3;
}).whenComplete((v, e) -> {
log.debug("结果:" + v);
}).exceptionally(e -> {
log.debug("出现异常:" + e.getMessage());
return null;
});
pool.shutdown();
}
}
22:46:05.153 c.TestThenApply [pool-1-thread-1] - 第一阶段
22:46:05.158 c.TestThenApply [pool-1-thread-1] - 结果:null
22:46:05.158 c.TestThenApply [pool-1-thread-1] - 出现异常:java.lang.ArithmeticException: / by zero
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
测试正常情况:
@Slf4j(topic = "c.TestHandle")
public class TestHandle {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
log.debug("第一阶段");
return 1;
}, pool).handle((v, e) -> {
log.debug("第二阶段");
return v + 2;
}).handle((v, e) -> {
log.debug("第三阶段");
if (e != null) {
log.debug("捕获上一阶段的异常:" + e.getMessage());
}
return v + 3;
}).whenComplete((v, e) -> {
log.debug("结果:" + v);
}).exceptionally(e -> {
log.debug("出现异常:" + e.getMessage());
return null;
});
pool.shutdown();
}
}
22:39:52.103 c.TestHandle [pool-1-thread-1] - 第一阶段
22:39:52.107 c.TestHandle [pool-1-thread-1] - 第二阶段
22:39:52.107 c.TestHandle [pool-1-thread-1] - 第三阶段
22:39:52.107 c.TestHandle [pool-1-thread-1] - 结果:6
测试异常情况:
@Slf4j(topic = "c.TestHandle")
public class TestHandle {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
log.debug("第一阶段");
return 1;
}, pool).handle((v, e) -> {
int i = 10 / 0;
log.debug("第二阶段");
return v + 2;
}).handle((v, e) -> {
log.debug("第三阶段");
if (e != null) {
log.debug("捕获上一阶段的异常:" + e.getMessage());
}
return v + 3;
}).whenComplete((v, e) -> {
log.debug("结果:" + v);
}).exceptionally(e -> {
log.debug("出现异常:" + e.getMessage());
return null;
});
pool.shutdown();
}
}
22:41:21.347 c.TestHandle [pool-1-thread-1] - 第一阶段
22:41:21.352 c.TestHandle [pool-1-thread-1] - 第三阶段
22:41:21.352 c.TestHandle [pool-1-thread-1] - 捕获上一阶段的异常:java.lang.ArithmeticException: / by zero
22:41:21.353 c.TestHandle [pool-1-thread-1] - 结果:null
22:41:21.353 c.TestHandle [pool-1-thread-1] - 出现异常:java.lang.NullPointerException
注意: 出现的空指针异常(v为null,出现在 return v + 2; 和 return v + 3;两处地方)
public <U> CompletionStage<U> thenApply(Function<? super T, ? extends U> fn);
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
这两个方法的返回值都是CompletionStage,只是传入函数fn不同
对于thenApply,fn函数是对一个已完成的CompletionStage的返回值进行计算、操作
对于thenCompose,fn函数是对新创建的CompletionStage进行计算、操作
测试:
public static void main(String[] args) {
CompletableFuture<String> thenApply = CompletableFuture.supplyAsync(() -> 1)
.thenApply(v -> "将Integer类型值:" + v + ",转换成字符串");
CompletableFuture<String> thenCompose = CompletableFuture.supplyAsync(() -> 1)
.thenCompose(v -> CompletableFuture.supplyAsync(() -> "新创建一个CompletableFuture将Integer类型值:" + v + ",转换成字符串"));
System.out.println("thenApply:" +thenApply.join());
System.out.println("thenCompose:" + thenCompose.join());
}
thenApply:将Integer类型值:1,转换成字符串
thenCompose:新创建一个CompletableFuture将Integer类型值:1,转换成字符串
whenComplete 源码:
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
测试:
@Slf4j(topic = "c.TestWhenComplete")
public class TestWhenComplete {
public static void main(String[] args) throws InterruptedException {
CompletableFuture.supplyAsync(() -> {
// 随机数
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("1s后返回结果:{}", i);
return i;
}).whenComplete((v, e) -> {
if (e == null) {
log.debug("没有异常,获取结果:{}", v);
}
}).exceptionally(e -> {
log.debug("出现异常:{}", e.getMessage());
return null;
});
log.debug("主线程去执行其他任务。。。");
log.debug("主线程执行完成");
}
}
18:18:51.912 c.TestWhenComplete [main] - 主线程去执行其他任务。。。
18:18:51.916 c.TestWhenComplete [main] - 主线程执行完成
Process finished with exit code 0
由结果可以看出,主线程停止后,子线程也随之停止
由于ForkJoinPool.commonPool()类似守护线程,还没执行完就随主线程一起关闭了
解决方法:
@Slf4j(topic = "c.TestWhenComplete")
public class TestWhenComplete {
public static void main(String[] args) throws InterruptedException {
CompletableFuture.supplyAsync(() -> {
// 随机数
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("1s后返回结果:{}", i);
return i;
}).whenComplete((v, e) -> {
if (e == null) {
log.debug("没有异常,获取结果:{}", v);
}
}).exceptionally(e -> {
log.debug("出现异常:{}", e.getMessage());
return null;
});
log.debug("主线程去执行其他任务。。。");
log.debug("主线程执行完成");
// 默认的线程池 —— ForkJoinPool.commonPool()类似守护线程,主线程关闭后,也会随之关闭
TimeUnit.SECONDS.sleep(2);
log.debug("主线程阻塞2s");
}
}
18:23:24.503 c.TestWhenComplete [main] - 主线程去执行其他任务。。。
18:23:24.506 c.TestWhenComplete [main] - 主线程执行完成
18:23:25.509 c.TestWhenComplete [ForkJoinPool.commonPool-worker-9] - 1s后返回结果:8
18:23:25.511 c.TestWhenComplete [ForkJoinPool.commonPool-worker-9] - 没有异常,获取结果:8
18:23:26.519 c.TestWhenComplete [main] - 主线程阻塞2s
Process finished with exit code 0
@Slf4j(topic = "c.TestWhenComplete")
public class TestWhenComplete {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = null;
try {
pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
// 随机数
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("1s后返回结果:{}", i);
return i;
// 传入自定义线程池
}, pool).whenComplete((v, e) -> {
if (e == null) {
log.debug("没有异常,获取结果:{}", v);
}
}).exceptionally(e -> {
log.debug("出现异常:{}", e.getMessage());
return null;
});
log.debug("主线程去执行其他任务。。。");
log.debug("主线程执行完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
pool.shutdown();
}
}
}
18:37:39.968 c.TestWhenComplete [main] - 主线程去执行其他任务。。。
18:37:39.970 c.TestWhenComplete [main] - 主线程执行完成
18:37:40.973 c.TestWhenComplete [pool-1-thread-1] - 1s后返回结果:1
18:37:40.975 c.TestWhenComplete [pool-1-thread-1] - 没有异常,获取结果:1
@Slf4j(topic = "c.TestWhenComplete")
public class TestWhenComplete {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = null;
try {
pool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
// 随机数
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("1s后返回结果:{}", i);
if (i > 2) {
// 模拟异常
i = 10/0;
}
return i;
// 传入自定义线程池
}, pool).whenComplete((v, e) -> {
if (e == null) {
log.debug("没有异常,获取结果:{}", v);
} else {
log.debug("捕获上一阶段异常:{}", e.getMessage());
}
}).exceptionally(e -> {
log.debug("出现异常:{}", e.getMessage());
return null;
});
log.debug("主线程去执行其他任务。。。");
log.debug("主线程执行完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
pool.shutdown();
}
}
}
00:04:32.849 c.TestWhenComplete [main] - 主线程去执行其他任务。。。
00:04:32.851 c.TestWhenComplete [main] - 主线程执行完成
00:04:33.849 c.TestWhenComplete [pool-1-thread-1] - 1s后返回结果:4
00:04:33.854 c.TestWhenComplete [pool-1-thread-1] - 捕获上一阶段异常:java.lang.ArithmeticException: / by zero
00:04:33.854 c.TestWhenComplete [pool-1-thread-1] - 出现异常:java.lang.ArithmeticException: / by zero
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
测试:
@Slf4j(topic = "c.TestThenAccept")
public class TestThenAccept {
public static void main(String[] args) {
CompletableFuture<Void> thenAccept = CompletableFuture.supplyAsync(() -> {
log.debug("第一阶段");
return 1;
}).thenApply(v -> {
log.debug("第二阶段");
return v + 2;
}).thenApply(v -> {
log.debug("第三阶段");
return v + 3;
}).thenAccept(v -> {
log.debug("结果:" + v);
});
log.debug("返回结果:{}", thenAccept.join());
}
}
22:52:09.389 c.TestThenAccept [ForkJoinPool.commonPool-worker-1] - 第一阶段
22:52:09.394 c.TestThenAccept [ForkJoinPool.commonPool-worker-1] - 第二阶段
22:52:09.394 c.TestThenAccept [ForkJoinPool.commonPool-worker-1] - 第三阶段
22:52:09.394 c.TestThenAccept [ForkJoinPool.commonPool-worker-1] - 结果:6
22:52:09.394 c.TestThenAccept [main] - 返回结果:null
public CompletionStage<Void> thenRun(Runnable action);
测试:
@Slf4j(topic = "c.TestThenRun_ThenAccept_ThenApply")
public class TestThenRun_ThenAccept_ThenApply {
public static void main(String[] args) {
CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
return 1;
}).thenRun(() -> {
log.debug("做其他任务");
});
log.debug("thenRun的结果:" + completableFuture1.join());
log.debug("===================");
CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
return 1;
}).thenAccept((v) -> {
log.debug("获取上一阶段的值:" + v);
});
log.debug("thenAccept的结果:" + completableFuture2.join());
log.debug("===================");
CompletableFuture<Integer> completableFuture3 = CompletableFuture.supplyAsync(() -> {
return 1;
}).thenApply((v) -> {
log.debug("获取上一阶段的值:" + v);
return v + 2;
});
log.debug("thenApply的结果:" + completableFuture3.join());
}
}
22:58:08.055 c.TestThenRun_ThenAccept_ThenApply [main] - 做其他任务
22:58:08.059 c.TestThenRun_ThenAccept_ThenApply [main] - thenRun的结果:null
22:58:08.059 c.TestThenRun_ThenAccept_ThenApply [main] - ===================
22:58:08.060 c.TestThenRun_ThenAccept_ThenApply [main] - 获取上一阶段的值:1
22:58:08.061 c.TestThenRun_ThenAccept_ThenApply [main] - thenAccept的结果:null
22:58:08.061 c.TestThenRun_ThenAccept_ThenApply [main] - ===================
22:58:08.062 c.TestThenRun_ThenAccept_ThenApply [main] - 获取上一阶段的值:1
22:58:08.062 c.TestThenRun_ThenAccept_ThenApply [main] - thenApply的结果:3
注意:
给各阶段加休眠:
@Slf4j(topic = "c.TestThenRun_ThenAccept_ThenApply")
public class TestThenRun_ThenAccept_ThenApply {
public static void main(String[] args) {
CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}).thenRun(() -> {
log.debug("做其他任务");
});
log.debug("thenRun的结果:" + completableFuture1.join());
log.debug("===================");
CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}).thenAccept((v) -> {
log.debug("获取上一阶段的值:" + v);
});
log.debug("thenAccept的结果:" + completableFuture2.join());
log.debug("===================");
CompletableFuture<Integer> completableFuture3 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}).thenApply((v) -> {
log.debug("获取上一阶段的值:" + v);
return v + 2;
});
log.debug("thenApply的结果:" + completableFuture3.join());
}
}
23:03:51.729 c.TestThenRun_ThenAccept_ThenApply [ForkJoinPool.commonPool-worker-1] - 做其他任务
23:03:51.732 c.TestThenRun_ThenAccept_ThenApply [main] - thenRun的结果:null
23:03:51.732 c.TestThenRun_ThenAccept_ThenApply [main] - ===================
23:03:53.734 c.TestThenRun_ThenAccept_ThenApply [ForkJoinPool.commonPool-worker-1] - 获取上一阶段的值:1
23:03:53.734 c.TestThenRun_ThenAccept_ThenApply [main] - thenAccept的结果:null
23:03:53.734 c.TestThenRun_ThenAccept_ThenApply [main] - ===================
23:03:56.735 c.TestThenRun_ThenAccept_ThenApply [ForkJoinPool.commonPool-worker-1] - 获取上一阶段的值:1
23:03:56.735 c.TestThenRun_ThenAccept_ThenApply [main] - thenApply的结果:3
thenRunAsync 源码:
// CPU核数是否大于1
private static final boolean useCommonPool =(ForkJoinPool.getCommonPoolParallelism() > 1);
// CPU核数大于1,则使用默认线程池 ForkJoinPool.commonPool()
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
public CompletableFuture<Void> thenRunAsync(Runnable action) {
// CPU核数大于1, asyncPool 使用默认线程池 ForkJoinPool.commonPool()
return uniRunStage(asyncPool, action);
}
测试 thenRun:
@Slf4j(topic = "c.TestThenRun")
public class TestThenRun {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
log.debug("不传入自定义线程池时,supplyAsync 的线程池:" + Thread.currentThread().getName());
return 1;
}).thenRun(() -> {
log.debug("thenRun 和上一阶段使用相同的线程池:" + Thread.currentThread().getName());
});
log.debug("结果:" + completableFuture1.join());
log.debug("======================");
CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
log.debug("传入自定义线程池时,supplyAsync 的线程池:" + Thread.currentThread().getName());
return 1;
// 传入自定义线程池
}, pool).thenRun(() -> {
log.debug("thenRun 和上一阶段使用相同的线程池:" + Thread.currentThread().getName());
});
log.debug("结果:" + completableFuture2.join());
log.debug("======================");
// 关闭线程池
pool.shutdown();
}
}
23:08:39.413 c.TestThenRun [ForkJoinPool.commonPool-worker-1] - 不传入自定义线程池时,supplyAsync 的线程池:ForkJoinPool.commonPool-worker-1
23:08:39.424 c.TestThenRun [ForkJoinPool.commonPool-worker-1] - thenRun 和上一阶段使用相同的线程池:ForkJoinPool.commonPool-worker-1
23:08:39.425 c.TestThenRun [main] - 结果:null
23:08:39.425 c.TestThenRun [main] - ======================
23:08:39.426 c.TestThenRun [pool-1-thread-1] - 传入自定义线程池时,supplyAsync 的线程池:pool-1-thread-1
23:08:39.426 c.TestThenRun [pool-1-thread-1] - thenRun 和上一阶段使用相同的线程池:pool-1-thread-1
23:08:39.426 c.TestThenRun [main] - 结果:null
23:08:39.426 c.TestThenRun [main] - ======================
测试 thenRunAsync:
@Slf4j(topic = "c.TestThenRunAsync")
public class TestThenRunAsync {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
log.debug("不传入自定义线程池时,supplyAsync 的线程池:" + Thread.currentThread().getName());
return 1;
}).thenRunAsync(() -> {
log.debug("thenRunAsync 使用默认的线程池:" + Thread.currentThread().getName());
});
log.debug("结果:" + completableFuture1.join());
log.debug("======================");
CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
log.debug("传入自定义线程池时,supplyAsync 的线程池:" + Thread.currentThread().getName());
return 1;
// 传入自定义线程池
}, pool).thenRunAsync(() -> {
log.debug("thenRunAsync 使用默认的线程池:" + Thread.currentThread().getName());
});
log.debug("结果:" + completableFuture2.join());
log.debug("======================");
// 关闭线程池
pool.shutdown();
}
}
23:10:21.374 c.TestThenRunAsync [ForkJoinPool.commonPool-worker-1] - 不传入自定义线程池时,supplyAsync 的线程池:ForkJoinPool.commonPool-worker-1
23:10:21.377 c.TestThenRunAsync [ForkJoinPool.commonPool-worker-1] - thenRunAsync 使用默认的线程池:ForkJoinPool.commonPool-worker-1
23:10:21.377 c.TestThenRunAsync [main] - 结果:null
23:10:21.377 c.TestThenRunAsync [main] - ======================
23:10:21.378 c.TestThenRunAsync [pool-1-thread-1] - 传入自定义线程池时,supplyAsync 的线程池:pool-1-thread-1
23:10:21.378 c.TestThenRunAsync [ForkJoinPool.commonPool-worker-1] - thenRunAsync 使用默认的线程池:ForkJoinPool.commonPool-worker-1
23:10:21.378 c.TestThenRunAsync [main] - 结果:null
23:10:21.379 c.TestThenRunAsync [main] - ======================
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn);
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action);
public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action);
测试 applyToEither:
@Slf4j(topic = "c.TestApplyToEither")
public class TestApplyToEither {
public static void main(String[] args) {
CompletableFuture<String> A = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("A 1s 完成任务");
return "A";
});
CompletableFuture<String> B = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("B 2s 完成任务");
return "B";
});
CompletableFuture<String> result = A.applyToEither(B, v -> v + " 先完成");
log.debug(result.join());
}
}
23:15:50.711 c.TestApplyToEither [ForkJoinPool.commonPool-worker-1] - A 1s 完成任务
23:15:50.714 c.TestApplyToEither [main] - A 先完成
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
测试 thenCombine:
@Slf4j(topic = "c.TestThenCombine")
public class TestThenCombine {
public static void main(String[] args) {
log.debug("开始");
CompletableFuture<Integer> A = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("A 完成");
return 10;
});
CompletableFuture<Integer> B = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("B 完成");
return 20;
});
CompletableFuture<Integer> result = A.thenCombine(B, (x, y) -> x + y);
log.debug("结果:" + result.join());
}
}
16:47:50.931 c.TestThenCombine [main] - 开始
16:47:51.992 c.TestThenCombine [ForkJoinPool.commonPool-worker-9] - A 完成
16:47:53.007 c.TestThenCombine [ForkJoinPool.commonPool-worker-2] - B 完成
16:47:53.007 c.TestThenCombine [main] - 结果:30
@Slf4j(topic = "c.TestThenCombine")
public class TestThenCombine {
public static void main(String[] args) {
log.debug("开始");
CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("A 完成");
return 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("B 完成");
return 20;
}), (x, y) -> x + y);
log.debug("结果:" + result.join());
}
}
16:53:50.817 c.TestThenCombine [main] - 开始
16:53:51.894 c.TestThenCombine [ForkJoinPool.commonPool-worker-9] - A 完成
16:53:52.886 c.TestThenCombine [ForkJoinPool.commonPool-worker-2] - B 完成
16:53:52.886 c.TestThenCombine [main] - 结果:30
分析 CompletableFuture 完成异步任务
当 任务数 等于 CompletableFuture 线程池的最大线程数(CPU核心数 - 1) 时,CompletableFuture 性能最佳
例如:有4个核心,则 CompletableFuture 线程池的最大线程数是3,此时任务数为3(假设每个任务耗时1s),CompletableFuture 性能最 佳(CompletableFuture 做完3个任务耗时也就1s左右);
如果任务数为4-6,则 CompletableFuture 做完4-6个任务耗时就会是2s左右;
如果任务数为7-9,则 CompletableFuture 做完7-9个任务耗时就会是3s左右;
以此类推
所以当已知任务数量时,可以创建自定义线程池,手动设置线程池最大线程数
如果需要根据任务数量动态设置线程池最大线程数时,每执行完一批任务后,需要关闭线程池,等下一次新任务来时再根据任务数重新创建线程池
例如:第一次来了100个任务,则创建10个线程的线程池,执行完100个任务后关闭线程池;第二次来了200个任务时,再重新创建有20个线程的线程池;
创建任务类:
@Slf4j(topic = "c.Task")
class Task {
// 任务名
private String name;
// 做该任务耗时 (秒)
private Integer productionTime;
public Task(String name, Integer productionTime) {
this.name = name;
this.productionTime = productionTime;
}
// 做任务
public void doTask() {
try {
TimeUnit.SECONDS.sleep(this.productionTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(this.name + " 已做完");
}
}
@Slf4j(topic = "c.TestCompletableFuture_doTask")
public class TestCompletableFuture_doTask {
public static void main(String[] args) {
log.debug("该二手台式机可用处理器数量:{}", Runtime.getRuntime().availableProcessors());
log.debug("当前线程池中的最大线程数:{}", ForkJoinPool.getCommonPoolParallelism());
log.debug("开始");
long startTime = System.currentTimeMillis();
// 创建任务
List<Task> tasks = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
Task task = new Task("任务" + i, 1);
tasks.add(task);
}
// 做任务
List<CompletableFuture> completableFutureList = new ArrayList<>();
for (Task task : tasks) {
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> task.doTask());
// 将所有任务加到 CompletableFuture 集合中
completableFutureList.add(cf);
}
// 等待 CompletableFuture 集合中的所有任务执行完毕
CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()])).join();
log.debug("做完所有任务的耗时:{}", (System.currentTimeMillis() - startTime));
}
}
00:50:41.392 c.TestCompletableFuture_doTask [main] - 该二手台式机可用处理器数量:4
00:50:41.392 c.TestCompletableFuture_doTask [main] - 当前线程池中的最大线程数:3
00:50:41.397 c.TestCompletableFuture_doTask [main] - 开始
00:50:42.467 c.Task [ForkJoinPool.commonPool-worker-3] - 任务3 已做完
00:50:42.467 c.Task [ForkJoinPool.commonPool-worker-2] - 任务2 已做完
00:50:42.467 c.Task [ForkJoinPool.commonPool-worker-1] - 任务1 已做完
00:50:42.467 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:1069
@Slf4j(topic = "c.TestCompletableFuture_doTask")
public class TestCompletableFuture_doTask {
public static void main(String[] args) {
log.debug("该二手台式机可用处理器数量:{}", Runtime.getRuntime().availableProcessors());
log.debug("当前线程池中的最大线程数:{}", ForkJoinPool.getCommonPoolParallelism());
log.debug("开始");
long startTime = System.currentTimeMillis();
// 将创建的任务加到 CompletableFuture 数组中 并执行
CompletableFuture[] tasks= IntStream.rangeClosed(1, 3)
.mapToObj(i -> new Task("任务" + i, 1))
.map(task -> CompletableFuture.runAsync(task::doTask))
.toArray(CompletableFuture[]::new);
// 等待 CompletableFuture 数组中的所有任务执行完
CompletableFuture.allOf(tasks).join();
log.debug("做完所有任务的耗时:{}", (System.currentTimeMillis() - startTime));
}
}
00:51:13.655 c.TestCompletableFuture_doTask [main] - 该二手台式机可用处理器数量:4
00:51:13.655 c.TestCompletableFuture_doTask [main] - 当前线程池中的最大线程数:3
00:51:13.667 c.TestCompletableFuture_doTask [main] - 开始
00:51:14.896 c.Task [ForkJoinPool.commonPool-worker-2] - 任务2 已做完
00:51:14.896 c.Task [ForkJoinPool.commonPool-worker-1] - 任务1 已做完
00:51:14.896 c.Task [ForkJoinPool.commonPool-worker-3] - 任务3 已做完
00:51:14.896 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:1229
@Slf4j(topic = "c.TestCompletableFuture_doTask")
public class TestCompletableFuture_doTask {
public static void main(String[] args) {
log.debug("该二手台式机可用处理器数量:{}", Runtime.getRuntime().availableProcessors());
log.debug("当前线程池中的最大线程数:{}", ForkJoinPool.getCommonPoolParallelism());
log.debug("开始");
long startTime = System.currentTimeMillis();
// 将创建的任务加到 CompletableFuture 数组中 并执行
CompletableFuture[] tasks= IntStream.rangeClosed(1, 3)
.mapToObj(i -> new Task("任务" + i, 1))
.map(task -> CompletableFuture.runAsync(task::doTask))
.toArray(CompletableFuture[]::new);
// 等待 CompletableFuture 数组中的所有任务执行完
CompletableFuture.allOf(tasks).join();
log.debug("做完所有任务的耗时:{}", (System.currentTimeMillis() - startTime));
}
}
01:06:31.516 c.TestCompletableFuture_doTask [main] - 该二手台式机可用处理器数量:4
01:06:31.516 c.TestCompletableFuture_doTask [main] - 当前线程池中的最大线程数:3
01:06:31.527 c.TestCompletableFuture_doTask [main] - 开始
01:06:32.636 c.Task [ForkJoinPool.commonPool-worker-2] - 任务2 已做完
01:06:32.636 c.Task [ForkJoinPool.commonPool-worker-1] - 任务1 已做完
01:06:32.637 c.Task [ForkJoinPool.commonPool-worker-3] - 任务3 已做完
01:06:33.637 c.Task [ForkJoinPool.commonPool-worker-2] - 任务4 已做完
01:06:33.637 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:2110
@Slf4j(topic = "c.TestCompletableFuture_doTask")
public class TestCompletableFuture_doTask {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
log.debug("开始");
long startTime = System.currentTimeMillis();
// 将创建的任务加到 CompletableFuture 数组中 并执行
CompletableFuture[] tasks= IntStream.rangeClosed(1, 3)
.mapToObj(i -> new Task("任务" + i, 1))
.map(task -> CompletableFuture.runAsync(task::doTask, pool)) // 传入自定义线程池
.toArray(CompletableFuture[]::new);
// 等待 CompletableFuture 数组中的所有任务执行完
CompletableFuture.allOf(tasks).join();
log.debug("做完所有任务的耗时:{}", (System.currentTimeMillis() - startTime));
pool.shutdown();
}
}
01:32:25.794 c.TestCompletableFuture_doTask [main] - 开始
01:32:26.871 c.Task [pool-1-thread-10] - 任务10 已做完
01:32:26.872 c.Task [pool-1-thread-9] - 任务9 已做完
01:32:26.872 c.Task [pool-1-thread-8] - 任务8 已做完
01:32:26.872 c.Task [pool-1-thread-7] - 任务7 已做完
01:32:26.872 c.Task [pool-1-thread-6] - 任务6 已做完
01:32:26.873 c.Task [pool-1-thread-5] - 任务5 已做完
01:32:26.873 c.Task [pool-1-thread-4] - 任务4 已做完
01:32:26.873 c.Task [pool-1-thread-3] - 任务3 已做完
01:32:26.874 c.Task [pool-1-thread-2] - 任务2 已做完
01:32:26.874 c.Task [pool-1-thread-1] - 任务1 已做完
01:32:26.874 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:1080
可以看到就算做10个任务,耗时也是1s左右,但不是任何数量的任务都只耗时1s
一台计算机,一瞬间处理的最大任务数量跟CPU的处理能力有关,也跟内存、硬盘、网卡等设备有关
具体跟什么有关,取决于任务本身所需要的资源
由于测试案例中的任务使用sleep模拟耗时的,但是sleep这个操作不太占CPU资源,
所以当传入自定义线程池后,任务数超过了CPU核心数后,耗时也不多。
01:06:33.637 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:2110
## CompletableFuture 配合自定义线程池使用
```java
@Slf4j(topic = "c.TestCompletableFuture_doTask")
public class TestCompletableFuture_doTask {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
log.debug("开始");
long startTime = System.currentTimeMillis();
// 将创建的任务加到 CompletableFuture 数组中 并执行
CompletableFuture[] tasks= IntStream.rangeClosed(1, 3)
.mapToObj(i -> new Task("任务" + i, 1))
.map(task -> CompletableFuture.runAsync(task::doTask, pool)) // 传入自定义线程池
.toArray(CompletableFuture[]::new);
// 等待 CompletableFuture 数组中的所有任务执行完
CompletableFuture.allOf(tasks).join();
log.debug("做完所有任务的耗时:{}", (System.currentTimeMillis() - startTime));
pool.shutdown();
}
}
01:32:25.794 c.TestCompletableFuture_doTask [main] - 开始
01:32:26.871 c.Task [pool-1-thread-10] - 任务10 已做完
01:32:26.872 c.Task [pool-1-thread-9] - 任务9 已做完
01:32:26.872 c.Task [pool-1-thread-8] - 任务8 已做完
01:32:26.872 c.Task [pool-1-thread-7] - 任务7 已做完
01:32:26.872 c.Task [pool-1-thread-6] - 任务6 已做完
01:32:26.873 c.Task [pool-1-thread-5] - 任务5 已做完
01:32:26.873 c.Task [pool-1-thread-4] - 任务4 已做完
01:32:26.873 c.Task [pool-1-thread-3] - 任务3 已做完
01:32:26.874 c.Task [pool-1-thread-2] - 任务2 已做完
01:32:26.874 c.Task [pool-1-thread-1] - 任务1 已做完
01:32:26.874 c.TestCompletableFuture_doTask [main] - 做完所有任务的耗时:1080
可以看到就算做10个任务,耗时也是1s左右,但不是任何数量的任务都只耗时1s
一台计算机,一瞬间处理的最大任务数量跟CPU的处理能力有关,也跟内存、硬盘、网卡等设备有关
具体跟什么有关,取决于任务本身所需要的资源
由于测试案例中的任务使用sleep模拟耗时的,但是sleep这个操作不太占CPU资源,
所以当传入自定义线程池后,任务数超过了CPU核心数后,耗时也不多。