一 案例
1.1 背景描述
对公交易服务使用了热点资源组件,出现了在高并发下触发线程池资源耗尽,任务堆积;出现内存oom。
1.2 模拟场景
public class OrderSystemCrash {// 模拟高并发场景public static void main(String[] args) {for (int i = 0; i < Integer.MAX_VALUE; i++) {processPayment();}// 阻塞主线程观察结果try {Thread.sleep(Long.MAX_VALUE);} catch (InterruptedException e) {}}// 模拟订单服务接口:支付完成后发送通知public static void processPayment() {// 致命点:使用默认线程池 ForkJoinPool.commonPool()CompletableFuture.runAsync(() -> {// 1. 查询订单(模拟耗时操作)queryOrder();// 2. 支付(模拟阻塞IO)pay();// 3. 发送通知(模拟网络请求)sendNotification();});}// 模拟数据库查询(耗时100ms)private static void queryOrder() {try {Thread.sleep(100);} catch (InterruptedException e) {}}// 模拟支付接口(耗时500ms)private static void pay() {try {Thread.sleep(500);} catch (InterruptedException e) {}}// 模拟通知服务(耗时200ms)private static void sendNotification() {try {Thread.sleep(200);} catch (InterruptedException e) {}}
}
报错信息如下:https://mp.weixin.qq.com/s/YgkLTrSeaNNSZqpUcEld7w
1.3 源码分析
Completablefuture不指定线程池时,调用runAsync 或者supplyAsync 。默认调用的fookjoinpool线程池;
CompletableFuture.runAsync(Runnable runnable)
或者 CompletableFuture.supplyAsync(Supplier<U> supplier)
自定义线程池:
1.4 原因
ForkJoinPool.commonPool() 的致命缺陷:
1全局共享:ForkJoinPool.commonPool()是 JVM 全局共享的线程池,所有未指定线程池的 CompletableFuture 任务和并行流(parallelStream())都会共享它。
2.无界队列,内存溢出导火锁:ForkJoinPool.commonPool()使用无界队列,理论上能存储大量任务,但实际受内存限制。大量任务到来时,队列会不断消耗内存,一旦超过系统承受能力,会触发 OutOfMemoryError,服务直接宕机。
1.5 解决办法
自定义线程池+自定义有界队列linkedblockedqueue+自定义拒绝策略+异常捕获。
修正后的代码
public class OrderSystemFix {// 1. 自定义线程池(核心参数:核心线程数=50,队列容量=1000,拒绝策略=降级)private static final ExecutorService orderPool = new ThreadPoolExecutor(50, 50, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(1000), // 有界队列new ThreadPoolExecutor.AbortPolicy() { // 自定义拒绝策略@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 记录日志 + 降级处理System.err.println("任务被拒绝,触发降级");// 异步重试或写入死信队列}});// 2. 修复后的订单服务public static void processPayment() {CompletableFuture.runAsync(() -> {try {queryOrder();pay();sendNotification();} catch (Exception e) {// 3. 异常捕获 + 降级System.err.println("支付流程异常:" + e.getMessage());}}, orderPool); // 关键:显式指定线程池}// 其他代码同上...
}