Future模式与CompletableFuture
处理异步任务时,Future
与CompletableFuture
是强有力的工具。
实战案例:多API并行调用
假设我们需要从多个微服务获取数据,然后合并结果:
public UserProfileDto getUserProfile(Long userId) {
long startTime = System.currentTimeMillis();
// 并行获取用户基本信息
CompletableFuture<UserBasicInfo> basicInfoFuture = CompletableFuture
.supplyAsync(() -> userService.getBasicInfo(userId));
// 并行获取用户订单信息
CompletableFuture<List<Order>> ordersFuture = CompletableFuture
.supplyAsync(() -> orderService.getUserOrders(userId));
// 并行获取用户积分信息
CompletableFuture<PointsInfo> pointsFuture = CompletableFuture
.supplyAsync(() -> pointsService.getUserPoints(userId));
// 等待所有任务完成并合并结果
UserProfileDto result = CompletableFuture
.allOf(basicInfoFuture, ordersFuture, pointsFuture)
.thenApply(v -> {
UserBasicInfo basicInfo = basicInfoFuture.join();
List<Order> orders = ordersFuture.join();
PointsInfo points = pointsFuture.join();
return new UserProfileDto(basicInfo, orders, points);
})
.join();
long endTime = System.currentTimeMillis();
log.info("获取用户档案总耗时: {}ms", (endTime - startTime));
return result;
}
使用CompletableFuture
可以将原本串行执行的三个服务调用并行化,显著提升响应速度。
线程池的正确使用姿势
线程池是Java并发编程的重要组件,但使用不当会导致严重问题。
线程池核心参数详解
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
timeUnit, // 时间单位
workQueue, // 工作队列
threadFactory, // 线程工厂
rejectedExecutionHandler // 拒绝策略
);
真实踩坑:线程池参数配置不当
某支付系统中,开发人员使用了这样的线程池:
// 错误示范
ExecutorService executor = Executors.newFixedThreadPool(10);
在高峰期,系统大量请求堆积,导致内存溢出。原因是newFixedThreadPool
使用的是无界队列LinkedBlockingQueue
,请求不断堆积最终耗尽内存。
正确的做法是明确指定队列大小,并设置合适的拒绝策略:
int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 60L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);
ThreadFactory threadFactory = new CustomThreadFactory("payment-thread-");
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
ExecutorService executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue, threadFactory, handler);
这个配置限制了队列大小,并使用CallerRunsPolicy
作为拒绝策略,在系统过载时让调用者线程执行任务,起到限流作用。
总结
点赞关注「佩奇的技术笔记」,获取更多并发编程实战技巧!