面试题答案
一键面试线程池参数配置
- 核心线程数(corePoolSize):根据业务场景中预计的常驻任务数量来设置。如果任务数量相对稳定,可设置为该稳定值。例如,若预计同时处理的任务数量通常在10个左右,可将
corePoolSize
设为10。这能确保这些任务能立即被处理,而无需等待新线程创建。 - 最大线程数(maximumPoolSize):考虑业务高峰期可能出现的最大任务并发量。比如,业务高峰期可能有100个任务同时到达,且这些任务处理时间较短,可将
maximumPoolSize
设为100。要注意,设置过大可能导致系统资源耗尽。 - 队列容量(workQueue):根据任务的特性选择合适的队列。如果任务处理时间短且数量多,可选用有界队列,如
ArrayBlockingQueue
,设置一个合理的容量,如200,防止任务堆积过多占用过多内存。若任务处理时间较长,可使用无界队列LinkedBlockingQueue
,但需注意可能导致OOM。 - 线程存活时间(keepAliveTime):对于处理时间较短但并发量波动较大的场景,设置一个合适的
keepAliveTime
,如5秒。当线程数超过corePoolSize
时,空闲线程在这个时间后会被销毁,释放资源。
whenComplete保证数据一致性和系统稳定性
- 数据一致性:
whenComplete
方法在任务完成后执行,无论是正常完成还是异常结束。通过在whenComplete
中进行数据的合并、校验等操作,可以确保最终的数据状态是一致的。例如,如果任务是对数据库进行读写操作,在whenComplete
中可以进行事务的提交或回滚,保证数据的完整性。 - 系统稳定性:
whenComplete
可以捕获任务执行过程中的异常,避免异常未处理导致系统崩溃。在whenComplete
中可以进行异常的记录、重试等操作,增强系统的稳定性。
核心代码示例
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class CompletableFutureExample {
private static final AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) {
// 配置线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10,
100,
5,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200)
);
CompletableFuture.supplyAsync(() -> {
// 模拟任务执行
System.out.println("Task " + counter.incrementAndGet() + " is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Task " + counter.get() + " result";
}, executor)
.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("Task failed: " + ex.getMessage());
} else {
System.out.println("Task completed: " + result);
}
// 可以在这里进行数据一致性相关操作,如数据库事务处理等
});
// 关闭线程池
executor.shutdown();
}
}
在上述代码中:
- 首先配置了一个
ThreadPoolExecutor
,设置了核心线程数、最大线程数、线程存活时间和队列容量。 - 使用
CompletableFuture.supplyAsync
提交任务到线程池执行。 whenComplete
方法在任务完成后处理结果,无论是正常完成还是异常,都能确保系统的稳定性和数据一致性。最后关闭线程池。