admin管理员组文章数量:1037775
大厂常用工具类Completablefuture的面试题都有哪些?这次我都替你收集好了。
大家好,我是程序员牛肉。
最近在复习自己的简历,发现Completablefuture这个工具类怎么突然这么爱考了,牛客上的面经基本都在问这个工具类。因此今天我们就来专门总结一下Completablefuture常见的面试题。
关于什么是Completablefuture,我在之前有写过几篇文章。因此我们这篇文章就不做赘述了。如果你还不知道什么是Completablefuture的话可以看看下面的文章:
下次换你来拷打面试官!一文带你读懂企业常用异步编程核心工具类CompletableFuture
2025-01-29
还在用Future搞异步?快看看企业中常用的CompletableFuture是怎么用的!
2024-11-19
让我们进入正题,看看有哪些题是面试官爱考的。
01、Completablefuture的底层原理是什么,为什么它可以实现任务的编排?
CompletableFuture内部维护了一个volatile
修饰的result
字段存储计算结果或异常,以及一个Completion
类型的stack
字段构成的依赖链栈。每个Completion
对象代表一个待触发的依赖任务(如thenApply
、thenAccept
等方法创建的任务),通过链表结构串联形成任务编排的流水线。
当我们使用Completablefuture来编排异步任务的时候,我们可以认为本质上是维护了一个栈类型的链表:
同级的任务在一个栈中。各个栈之间使用链表相连。
比如当我们使用Completablefuture进行如下任务编排的时候:
代码语言:javascript代码运行次数:0运行复制CompletableFuture.supplyAsync(() -> 10)
.thenApply(r -> r * 1)
.thenApply(r -> r * 2)
.thenApply(r -> r * 3);
本质上在内部就维护了下图所示的一个链表:
每一次thenApply方法都会创建出一个completion,这个completion除了我们的lambda函数之外,还会有两个指针指向它的前置future和下一个future,所以当一个子任务完成之后,他将通过指针寻找自己的下一个completion对象,直至到末尾。
[整个过程通过 stack
链表构建了 CompletableFuture
之间的依赖关系,当一个 CompletableFuture
完成时,会根据 stack
链表依次触发后续依赖操作。每个 CompletableFuture
的计算结果存储在 result
字段中,通过这种方式实现了异步任务的链式调用和结果传递。]
02、Completablefuture都有哪些常用方法?
1.创建异步任务
代码语言:javascript代码运行次数:0运行复制//supplyAsync()
//执行有返回值的异步任务,默认使用 ForkJoinPool。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Hello World";
});
//runAsync()
//执行无返回值的异步任务。
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Task is running");
});
2.链式处理结果
代码语言:javascript代码运行次数:0运行复制//thenApply()
//对结果进行转换,返回新值。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World"); // 结果为 "Hello World"
//thenAccept()
//消费结果(无返回值)。
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(s -> System.out.println(s)); // 输出 "Hello"
//thenRun()
//任务完成后执行一个动作(不依赖结果)。
CompletableFuture.supplyAsync(() -> "Hello")
.thenRun(() -> System.out.println("Task Done"));
3.组合多个future
代码语言:javascript代码运行次数:0运行复制//thenCompose()
//扁平化嵌套的 CompletableFuture。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));
//thenCombine()
//合并两个独立 Future 的结果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
CompletableFuture<String> combined = future1.thenCombine(future2, (s1, s2) -> s1 + s2);
4.多任务协作
代码语言:javascript代码运行次数:0运行复制//allOf()
//等待所有 Future 完成。
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
all.thenRun(() -> System.out.println("All tasks done"));
//anyOf()
//任意一个 Future 完成即触发。
CompletableFuture<Object> any = CompletableFuture.anyOf(future1, future2);
any.thenAccept(result -> System.out.println("First result: " + result));
5.超时控制:
代码语言:javascript代码运行次数:0运行复制//orTimeout()
//超时抛出 TimeoutException。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> longTask())
.orTimeout(1, TimeUnit.SECONDS);
//completeOnTimeout()
//超时返回默认值。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> longTask())
pleteOnTimeout("Timeout", 1, TimeUnit.SECONDS);
03、如何使用Completablefuture实现调用接口的时候有超时控制机制,如果接口超时就取消执行任务的线程?
这个题本质上还是在考察我们对Completablefuture相关方法的熟练程度。设计这个功能其实并不困难。
从逻辑的角度讲,应该是设置超时时间,当接口调用超时之后就抛异常。之后我们在catch中手动的捕捉这个异常并且中断相关的线程。
而基于Completablefuture的相关方法,具体的代码设计为:先通过orTimeout
方法来实现。如果接口调用超时,可以通过cancel
方法来中断执行任务的线程。
public class CompletableFutureTimeoutExample {
public static void main(String[] args) {
// 创建CompletableFuture来执行异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟接口调用
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return"Response from the server";
});
// 设置超时时间
CompletableFuture<String> timeoutFuture = future.orTimeout(3, TimeUnit.SECONDS);
try {
// 获取结果,如果超时会抛出TimeoutException
String result = timeoutFuture.get();
System.out.println("Result: " + result);
} catch (TimeoutException e) {
System.out.println("Timeout occurred, interrupting the task.");
// 取消任务,中断线程
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
04、Completablefuture是如何实现带有超时时间的等待任务的?
前面我们说过Completablefuture内部会有一个volatile
修饰的result
字段存储计算结果或异常。在使用get方法的时候,先对result进行判断。如果result此时为空,就说明对应的异步任务还没有执行,调用timeGet方法进行后续操作。
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
//超时时间(我们输入的是秒,在这里要转化成为纳秒)
long nanos = unit.toNanos(timeout);
Object r;
//如果这个异步任务还没有执行,调用timeGet进行执行
if ((r = result) == null)
r = timedGet(nanos);
return (T) reportGet(r);
}
这个方法当对应的completablefuture任务还没有执行的时候,就会使用timeget方法。
那我们先不着急看timeGet的源码,先自己理一理timeGet的逻辑:
get方法的意思是超时等待。那既然result结果为空就要进入timeGet方法进行超时等待了。所以timeGet方法的大致逻辑应该就是一直等待对应的异步任务有执行结果(result不为空)或者等待超时。让我们来看源码:
首先判断当前的线程有没有被中断,如果你都被中断了就直接返回null。
之后再看传递过来的时间参数是否异常,不能小于或者等于0。如果小于或者等于0的话直接抛出超时异常:
让我们继续看如果传递的参数是正常的逻辑,先对传递过来的超时参数进行了处理,让其加上当前的纳秒数。获取到一个以纳秒为单位的截止时间。
之后为了取得有效时间,又进行了一次判断。如果d==0的话,就给他赋值1纳秒。
并且我们创建了一个等待节点Singnaller,封装当前线程、剩余超时时间和截止时间。
之后就开始死等了,外层就是一个while循环,循环结束的条件是result不为空:
让我们开始看内部的代码,首先当对应的等待节点q为空的时候就开始封装对应的等待节点,添加了封装当前线程、剩余超时时间和截止时间。
这里其实是一个优化后的阻塞操作。当我们封装好了对应的q节点之后,最简单的操作就是直接挂起对应的线程,等待任务的执行。
但如果当前的线程是ForkJoinWorkerThread(FixedThreadPool的工作线程)
的时候,为了避免大量的工作线程都被阻塞而导致任务阻塞的情况,我们就使用helpAsyncBlocker方法来让线程在等待期间协作执行其他任务,避免完全挂起,从而提升资源利用率。
问题来了:为啥这里要单独对ForkJoinWorkerThread这个线程做优化呢?
原因是因为Completablefuture的场景本来就适合异步任务的编排。而如果未手动指定线程池,所有所有 supplyAsync
、thenApply
等操作默认由 ForkJoinPool
的 ForkJoinWorkerThread
执行。
CompletableFuture.supplyAsync(() -> doWork())
.thenAccept(result -> {
CompletableFuture<Void> nestedFuture = CompletableFuture.runAsync(() -> {
// 另一个耗时操作
processResult(result);
});
try {
// 在回调中使用带超时的 get()
nestedFuture.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("嵌套任务超时!");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
因此我们要单独对ForkJoinPool的线程进行优化。但需要注意的是:Completablefuture只有在两核以及两核以上的服务器中使用ForkJoinPool,如果不符合这个条件,它使用的是:ThreadPerTaskExecutor。(这个后面会详细说)
让我们继续回到代码:
在做了优化之后,将对应的的等待节点压入栈中,并且进行超时检查:
之后开始阻塞当前的线程:
阻塞的时候使用的是managedBlock这个方法,在这里我们只关注这个block方法:
由于我们传递的是等待节点Singnaller,所以他在这里实际上调用的是等待节点Singnaller的block方法:
这一看太熟悉了,这不是park方法吗?如果没有超时时间就是用park,如果有超时时间就是使用带有超时时间的parkNanos。
而timedget的后面那些就是进行一些删除操作,清除掉已经超时的无效等待节点之类的操作:
所以说Completablefuture带有超时时间的get的底层设计思路就是:如果当前线程是forkJoinPool的线程,就使用helpAsyncBlocker来进行挂起并优化。
如果是普通线程的话,就先将其加入到栈中,然后直接使用带有超时时间的park方法将其挂起。
最后再清除一些栈中的无效节点,做一下内存优化工作。
05、在上面的问题中,你提到了Completablefuture的线程池,你能详细的讲一讲吗?
从代码中我们可以看出它内部的线程池一共有两种:forkJoinPool和threadPerTaskExecutor。
我们可以看到:默认线程池是根据USE_COMMON_POOL这个布尔值来进行选择的。
- 如果
USE_COMMON_POOL
为true
,则ASYNC_POOL
被设置为ForkJoinPoolmonPool()
。 - 如果
USE_COMMON_POOL
为false
,则ASYNC_POOL
被设置为一个ThreadPerTaskExecutor
。
[ThreadPerTaskExecutor
是一个特殊的线程池实现,其核心设计思想是为每个提交的任务创建一个独立的线程来执行。这种设计特别适合轻量级任务的执行,理论上讲可以创建无数个线程,但实际还是受限于系统资源。]
那么USE_COMMON_POOL是如何进行取值的呢?
它使用的是getCommonPoolPrarllelism来进行的判断,这个方法返回公共线程池的并行度,默认情况下,公共线程池的并行度等于系统的可用处理器数量。
默认情况下,ForkJoinPoolmonPool
的并行度等于系统的可用处理器数量减去1。
- 假设你的系统有 4 个核心,
ForkJoinPool.getCommonPoolParallelism()
返回 3,那么USE_COMMON_POOL
将被设置为true
,此时的默认线程池为ForkJoinPool。 - 假设你的系统有两个核心,
ForkJoinPool.getCommonPoolParallelism()
返回 1,那么USE_COMMON_POOL
将被设置为false
,此时的默认线程池为:ThreadPerTaskExecutor 。
也就是说CompletableFuture的默认线程池只有在双核以上的机器内才会使用。在双核及以下的机器中,会为每个任务创建一个新线程,等于没有使用线程池,且有资源耗尽的风险。
而且吧,就算是ForkJoinPool也会有很多槽点,因此在使用Complefuturetable的时候,强烈推荐使用自定义线程池。
终于讲完了,差点累死我。这些内容足以应对90%的Completablefuture面试题了。我也极力推荐大家多去看一看源码,对个人的能力提升还是挺大的。
今天的文章就聊到这里了,相信通过我的介绍,你已经大致了解了这些常问的面试题。希望我的文章可以帮到你。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-14,如有侵权请联系 cloudcommunity@tencent 删除异步异常工具类线程线程池大厂常用工具类Completablefuture的面试题都有哪些?这次我都替你收集好了。
大家好,我是程序员牛肉。
最近在复习自己的简历,发现Completablefuture这个工具类怎么突然这么爱考了,牛客上的面经基本都在问这个工具类。因此今天我们就来专门总结一下Completablefuture常见的面试题。
关于什么是Completablefuture,我在之前有写过几篇文章。因此我们这篇文章就不做赘述了。如果你还不知道什么是Completablefuture的话可以看看下面的文章:
下次换你来拷打面试官!一文带你读懂企业常用异步编程核心工具类CompletableFuture
2025-01-29
还在用Future搞异步?快看看企业中常用的CompletableFuture是怎么用的!
2024-11-19
让我们进入正题,看看有哪些题是面试官爱考的。
01、Completablefuture的底层原理是什么,为什么它可以实现任务的编排?
CompletableFuture内部维护了一个volatile
修饰的result
字段存储计算结果或异常,以及一个Completion
类型的stack
字段构成的依赖链栈。每个Completion
对象代表一个待触发的依赖任务(如thenApply
、thenAccept
等方法创建的任务),通过链表结构串联形成任务编排的流水线。
当我们使用Completablefuture来编排异步任务的时候,我们可以认为本质上是维护了一个栈类型的链表:
同级的任务在一个栈中。各个栈之间使用链表相连。
比如当我们使用Completablefuture进行如下任务编排的时候:
代码语言:javascript代码运行次数:0运行复制CompletableFuture.supplyAsync(() -> 10)
.thenApply(r -> r * 1)
.thenApply(r -> r * 2)
.thenApply(r -> r * 3);
本质上在内部就维护了下图所示的一个链表:
每一次thenApply方法都会创建出一个completion,这个completion除了我们的lambda函数之外,还会有两个指针指向它的前置future和下一个future,所以当一个子任务完成之后,他将通过指针寻找自己的下一个completion对象,直至到末尾。
[整个过程通过 stack
链表构建了 CompletableFuture
之间的依赖关系,当一个 CompletableFuture
完成时,会根据 stack
链表依次触发后续依赖操作。每个 CompletableFuture
的计算结果存储在 result
字段中,通过这种方式实现了异步任务的链式调用和结果传递。]
02、Completablefuture都有哪些常用方法?
1.创建异步任务
代码语言:javascript代码运行次数:0运行复制//supplyAsync()
//执行有返回值的异步任务,默认使用 ForkJoinPool。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Hello World";
});
//runAsync()
//执行无返回值的异步任务。
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Task is running");
});
2.链式处理结果
代码语言:javascript代码运行次数:0运行复制//thenApply()
//对结果进行转换,返回新值。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World"); // 结果为 "Hello World"
//thenAccept()
//消费结果(无返回值)。
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(s -> System.out.println(s)); // 输出 "Hello"
//thenRun()
//任务完成后执行一个动作(不依赖结果)。
CompletableFuture.supplyAsync(() -> "Hello")
.thenRun(() -> System.out.println("Task Done"));
3.组合多个future
代码语言:javascript代码运行次数:0运行复制//thenCompose()
//扁平化嵌套的 CompletableFuture。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));
//thenCombine()
//合并两个独立 Future 的结果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
CompletableFuture<String> combined = future1.thenCombine(future2, (s1, s2) -> s1 + s2);
4.多任务协作
代码语言:javascript代码运行次数:0运行复制//allOf()
//等待所有 Future 完成。
CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);
all.thenRun(() -> System.out.println("All tasks done"));
//anyOf()
//任意一个 Future 完成即触发。
CompletableFuture<Object> any = CompletableFuture.anyOf(future1, future2);
any.thenAccept(result -> System.out.println("First result: " + result));
5.超时控制:
代码语言:javascript代码运行次数:0运行复制//orTimeout()
//超时抛出 TimeoutException。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> longTask())
.orTimeout(1, TimeUnit.SECONDS);
//completeOnTimeout()
//超时返回默认值。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> longTask())
pleteOnTimeout("Timeout", 1, TimeUnit.SECONDS);
03、如何使用Completablefuture实现调用接口的时候有超时控制机制,如果接口超时就取消执行任务的线程?
这个题本质上还是在考察我们对Completablefuture相关方法的熟练程度。设计这个功能其实并不困难。
从逻辑的角度讲,应该是设置超时时间,当接口调用超时之后就抛异常。之后我们在catch中手动的捕捉这个异常并且中断相关的线程。
而基于Completablefuture的相关方法,具体的代码设计为:先通过orTimeout
方法来实现。如果接口调用超时,可以通过cancel
方法来中断执行任务的线程。
public class CompletableFutureTimeoutExample {
public static void main(String[] args) {
// 创建CompletableFuture来执行异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟接口调用
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return"Response from the server";
});
// 设置超时时间
CompletableFuture<String> timeoutFuture = future.orTimeout(3, TimeUnit.SECONDS);
try {
// 获取结果,如果超时会抛出TimeoutException
String result = timeoutFuture.get();
System.out.println("Result: " + result);
} catch (TimeoutException e) {
System.out.println("Timeout occurred, interrupting the task.");
// 取消任务,中断线程
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
04、Completablefuture是如何实现带有超时时间的等待任务的?
前面我们说过Completablefuture内部会有一个volatile
修饰的result
字段存储计算结果或异常。在使用get方法的时候,先对result进行判断。如果result此时为空,就说明对应的异步任务还没有执行,调用timeGet方法进行后续操作。
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
//超时时间(我们输入的是秒,在这里要转化成为纳秒)
long nanos = unit.toNanos(timeout);
Object r;
//如果这个异步任务还没有执行,调用timeGet进行执行
if ((r = result) == null)
r = timedGet(nanos);
return (T) reportGet(r);
}
这个方法当对应的completablefuture任务还没有执行的时候,就会使用timeget方法。
那我们先不着急看timeGet的源码,先自己理一理timeGet的逻辑:
get方法的意思是超时等待。那既然result结果为空就要进入timeGet方法进行超时等待了。所以timeGet方法的大致逻辑应该就是一直等待对应的异步任务有执行结果(result不为空)或者等待超时。让我们来看源码:
首先判断当前的线程有没有被中断,如果你都被中断了就直接返回null。
之后再看传递过来的时间参数是否异常,不能小于或者等于0。如果小于或者等于0的话直接抛出超时异常:
让我们继续看如果传递的参数是正常的逻辑,先对传递过来的超时参数进行了处理,让其加上当前的纳秒数。获取到一个以纳秒为单位的截止时间。
之后为了取得有效时间,又进行了一次判断。如果d==0的话,就给他赋值1纳秒。
并且我们创建了一个等待节点Singnaller,封装当前线程、剩余超时时间和截止时间。
之后就开始死等了,外层就是一个while循环,循环结束的条件是result不为空:
让我们开始看内部的代码,首先当对应的等待节点q为空的时候就开始封装对应的等待节点,添加了封装当前线程、剩余超时时间和截止时间。
这里其实是一个优化后的阻塞操作。当我们封装好了对应的q节点之后,最简单的操作就是直接挂起对应的线程,等待任务的执行。
但如果当前的线程是ForkJoinWorkerThread(FixedThreadPool的工作线程)
的时候,为了避免大量的工作线程都被阻塞而导致任务阻塞的情况,我们就使用helpAsyncBlocker方法来让线程在等待期间协作执行其他任务,避免完全挂起,从而提升资源利用率。
问题来了:为啥这里要单独对ForkJoinWorkerThread这个线程做优化呢?
原因是因为Completablefuture的场景本来就适合异步任务的编排。而如果未手动指定线程池,所有所有 supplyAsync
、thenApply
等操作默认由 ForkJoinPool
的 ForkJoinWorkerThread
执行。
CompletableFuture.supplyAsync(() -> doWork())
.thenAccept(result -> {
CompletableFuture<Void> nestedFuture = CompletableFuture.runAsync(() -> {
// 另一个耗时操作
processResult(result);
});
try {
// 在回调中使用带超时的 get()
nestedFuture.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("嵌套任务超时!");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
因此我们要单独对ForkJoinPool的线程进行优化。但需要注意的是:Completablefuture只有在两核以及两核以上的服务器中使用ForkJoinPool,如果不符合这个条件,它使用的是:ThreadPerTaskExecutor。(这个后面会详细说)
让我们继续回到代码:
在做了优化之后,将对应的的等待节点压入栈中,并且进行超时检查:
之后开始阻塞当前的线程:
阻塞的时候使用的是managedBlock这个方法,在这里我们只关注这个block方法:
由于我们传递的是等待节点Singnaller,所以他在这里实际上调用的是等待节点Singnaller的block方法:
这一看太熟悉了,这不是park方法吗?如果没有超时时间就是用park,如果有超时时间就是使用带有超时时间的parkNanos。
而timedget的后面那些就是进行一些删除操作,清除掉已经超时的无效等待节点之类的操作:
所以说Completablefuture带有超时时间的get的底层设计思路就是:如果当前线程是forkJoinPool的线程,就使用helpAsyncBlocker来进行挂起并优化。
如果是普通线程的话,就先将其加入到栈中,然后直接使用带有超时时间的park方法将其挂起。
最后再清除一些栈中的无效节点,做一下内存优化工作。
05、在上面的问题中,你提到了Completablefuture的线程池,你能详细的讲一讲吗?
从代码中我们可以看出它内部的线程池一共有两种:forkJoinPool和threadPerTaskExecutor。
我们可以看到:默认线程池是根据USE_COMMON_POOL这个布尔值来进行选择的。
- 如果
USE_COMMON_POOL
为true
,则ASYNC_POOL
被设置为ForkJoinPoolmonPool()
。 - 如果
USE_COMMON_POOL
为false
,则ASYNC_POOL
被设置为一个ThreadPerTaskExecutor
。
[ThreadPerTaskExecutor
是一个特殊的线程池实现,其核心设计思想是为每个提交的任务创建一个独立的线程来执行。这种设计特别适合轻量级任务的执行,理论上讲可以创建无数个线程,但实际还是受限于系统资源。]
那么USE_COMMON_POOL是如何进行取值的呢?
它使用的是getCommonPoolPrarllelism来进行的判断,这个方法返回公共线程池的并行度,默认情况下,公共线程池的并行度等于系统的可用处理器数量。
默认情况下,ForkJoinPoolmonPool
的并行度等于系统的可用处理器数量减去1。
- 假设你的系统有 4 个核心,
ForkJoinPool.getCommonPoolParallelism()
返回 3,那么USE_COMMON_POOL
将被设置为true
,此时的默认线程池为ForkJoinPool。 - 假设你的系统有两个核心,
ForkJoinPool.getCommonPoolParallelism()
返回 1,那么USE_COMMON_POOL
将被设置为false
,此时的默认线程池为:ThreadPerTaskExecutor 。
也就是说CompletableFuture的默认线程池只有在双核以上的机器内才会使用。在双核及以下的机器中,会为每个任务创建一个新线程,等于没有使用线程池,且有资源耗尽的风险。
而且吧,就算是ForkJoinPool也会有很多槽点,因此在使用Complefuturetable的时候,强烈推荐使用自定义线程池。
终于讲完了,差点累死我。这些内容足以应对90%的Completablefuture面试题了。我也极力推荐大家多去看一看源码,对个人的能力提升还是挺大的。
今天的文章就聊到这里了,相信通过我的介绍,你已经大致了解了这些常问的面试题。希望我的文章可以帮到你。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-14,如有侵权请联系 cloudcommunity@tencent 删除异步异常工具类线程线程池本文标签: 大厂常用工具类Completablefuture的面试题都有哪些这次我都替你收集好了
版权声明:本文标题:大厂常用工具类Completablefuture的面试题都有哪些?这次我都替你收集好了。 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1748253003a2275624.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论