异步编程与多线程
/**
* 背景:Java中多线程与异步编程是比较核心的特性,而且很多优秀组件源码中都将2者结合起来使用,为什么异步编程需要线程呢?相信很多JAVA初学者比较迷茫
* 核心原因:异步编程的本质是"同时做多件事",而线程是操作系统提供的"同时做多件事"的机制。
* 具体原因:物理限制;阻塞问题;资源利用;编程模型
**/
// 1. 物理限制: CPU在某个时刻只能执行一条指令;要让多个任务看起来"同时"执行,需要快速切换;线程就是操作系统提供的切换机制
// 2. 阻塞问题
// 同步执行 - 主线程被阻塞
String data = database.query(); // 等待2秒
String result = network.request(); // 又等待1秒
// 总共等待3秒,期间什么都做不了
// 异步执行 - 需要其他线程
Thread t1 = new Thread(() -> database.query());
Thread t2 = new Thread(() -> network.request());
t1.start(); t2.start();
// 主线程可以继续做其他事情
// 3. 资源利用: 当一个线程在等待I/O时,CPU可以切换到其他线程;多核CPU可以真正并行执行多个线程;没有线程,CPU就会空闲等待
// 4. 编程模型: 异步编程需要"发起任务后继续执行";这需要操作系统级别的支持;线程提供了这种执行上下文的切换能力.
CompletableFuture 基础
什么是 CompletableFuture
CompletableFuture 是 Java 8 引入的异步编程工具,实现了 Future 和 CompletionStage 接口,提供了强大的异步任务处理能力。
创建 CompletableFuture
// 创建已完成的 CompletableFuture
CompletableFuture<String> completed = CompletableFuture.completedFuture("Hello");
// 创建异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "异步任务完成";
});
// 无返回值的异步任务
CompletableFuture<Void> voidFuture = CompletableFuture.runAsync(() -> {
System.out.println("执行无返回值任务");
});
核心方法分类
供给型方法(Supplier)
// supplyAsync - 异步执行有返回值的任务
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
return "供给型任务结果";
});
// 使用自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
CompletableFuture<String> supplyWithExecutor = CompletableFuture.supplyAsync(() -> {
return "使用自定义线程池";
}, executor);
运行型方法(Runnable)
// runAsync - 异步执行无返回值的任务
CompletableFuture<Void> run = CompletableFuture.runAsync(() -> {
System.out.println("执行运行型任务");
});
换型方法(Function)
CompletableFuture<String> original = CompletableFuture.supplyAsync(() -> "Hello");
// thenApply - 对结果进行转换
CompletableFuture<String> transformed = original.thenApply(result -> result + " World");
// thenApplyAsync - 异步转换
CompletableFuture<String> asyncTransformed = original.thenApplyAsync(result -> {
return result.toUpperCase();
});
消费型方法(Consumer)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "消费型示例");
// thenAccept - 消费结果,无返回值
CompletableFuture<Void> consumed = future.thenAccept(result -> {
System.out.println("接收到结果: " + result);
});
// thenAcceptAsync - 异步消费
CompletableFuture<Void> asyncConsumed = future.thenAcceptAsync(result -> {
System.out.println("异步消费: " + result);
});
组合操作
串行组合
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "第一个");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "第二个");
// thenCompose - 串行组合,将第一个结果传递给第二个任务
CompletableFuture<String> composed = future1.thenCompose(result1 -> {
return CompletableFuture.supplyAsync(() -> result1 + " -> 组合结果");
});
// thenCombine - 两个任务都完成后,组合结果
CompletableFuture<String> combined = future1.thenCombine(future2, (result1, result2) -> {
return result1 + " + " + result2;
});
并行组合
// allOf - 等待所有任务完成
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2");
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "任务3");
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
CompletableFuture<List<String>> allResults = allTasks.thenApply(v -> {
return Arrays.asList(task1.join(), task2.join(), task3.join());
});
// anyOf - 任意一个任务完成即返回
CompletableFuture<Object> anyResult = CompletableFuture.anyOf(task1, task2, task3);
异常处理
基本异常处理
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("随机异常");
}
return "成功结果";
});
// exceptionally - 处理异常
CompletableFuture<String> handled = future.exceptionally(ex -> {
System.out.println("处理异常: " + ex.getMessage());
return "默认值";
});
// handle - 同时处理正常结果和异常
CompletableFuture<String> handleBoth = future.handle((result, ex) -> {
if (ex != null) {
return "异常处理: " + ex.getMessage();
}
return "正常结果: " + result;
});
链式异常处理
CompletableFuture<String> chainedHandling = CompletableFuture.supplyAsync(() -> {
return "初始值";
}).thenApply(result -> {
// 可能抛出异常的转换
if (result.length() < 10) {
throw new RuntimeException("长度不足");
}
return result.toUpperCase();
}).exceptionally(ex -> {
return "异常时的默认值";
}).thenApply(result -> {
return "最终结果: " + result;
});
线程池配置
默认线程池
// CompletableFuture 默认使用 ForkJoinPool.commonPool()
// 线程数量 = CPU 核心数 - 1
// 查看默认线程池信息
System.out.println("默认线程池大小: " + ForkJoinPool.getCommonPoolParallelism());
自定义线程池
// 创建自定义线程池
ExecutorService customExecutor = Executors.newFixedThreadPool(8, r -> {
Thread t = new Thread(r);
t.setName("CustomAsync-" + t.getId());
t.setDaemon(true);
return t;
});
// 使用自定义线程池
CompletableFuture<String> customFuture = CompletableFuture.supplyAsync(() -> {
return "使用自定义线程池: " + Thread.currentThread().getName();
}, customExecutor);
超时处理
Java 9+ 超时方法
// 注意:orTimeout 和 completeOnTimeout 是 Java 9 引入的
// Java 8 需要使用其他方式实现超时
// Java 8 实现超时的方法
public static <T> CompletableFuture<T> withTimeout(CompletableFuture<T> future,
long timeout,
TimeUnit unit) {
CompletableFuture<T> timeoutFuture = new CompletableFuture<>();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> {
timeoutFuture.completeExceptionally(new TimeoutException("操作超时"));
}, timeout, unit);
return future.applyToEither(timeoutFuture, Function.identity());
}
实际应用示例
并发调用多个服务
public class ServiceCaller {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public CompletableFuture<String> getUserInfo(String userId) {
return CompletableFuture.supplyAsync(() -> {
// 模拟调用用户服务
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "用户信息: " + userId;
}, executor);
}
public CompletableFuture<String> getOrderInfo(String userId) {
return CompletableFuture.supplyAsync(() -> {
// 模拟调用订单服务
try {
Thread.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "订单信息: " + userId;
}, executor);
}
public CompletableFuture<String> getCombinedInfo(String userId) {
CompletableFuture<String> userFuture = getUserInfo(userId);
CompletableFuture<String> orderFuture = getOrderInfo(userId);
return userFuture.thenCombine(orderFuture, (user, order) -> {
return "组合结果: " + user + ", " + order;
});
}
}
异步处理流水线
public class AsyncPipeline {
public CompletableFuture<String> processData(String input) {
return CompletableFuture.supplyAsync(() -> {
// 第一步:数据验证
if (input == null || input.isEmpty()) {
throw new IllegalArgumentException("输入不能为空");
}
return input.trim();
}).thenApplyAsync(data -> {
// 第二步:数据转换
return data.toUpperCase();
}).thenApplyAsync(data -> {
// 第三步:数据处理
return "处理后的数据: " + data;
}).exceptionally(ex -> {
// 异常处理
return "处理失败: " + ex.getMessage();
});
}
}
性能优化建议
线程池选择
CPU 密集型任务:线程数 = CPU 核心数 + 1
I/O 密集型任务:线程数 = CPU 核心数 * 2 或更多
根据实际测试结果调整线程池大小
避免阻塞
// 避免在异步任务中使用阻塞操作
CompletableFuture<String> bad = CompletableFuture.supplyAsync(() -> {
// 错误:在异步任务中使用阻塞操作
return anotherFuture.join(); // 会阻塞线程
});
// 正确写法:使用组合操作
CompletableFuture<String> good = CompletableFuture.supplyAsync(() -> {
return "初始数据";
}).thenCompose(data -> {
return anotherFuture; // 非阻塞组合
});
资源管理
// 确保及时关闭自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
try {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "任务结果";
}, executor);
String result = future.get(5, TimeUnit.SECONDS);
} finally {
executor.shutdown();
}
生产环境常见陷阱与注意事项
避免过度使用 get()
// 错误:频繁使用 get() 会导致阻塞
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "任务2");
// 不好的做法
String result1 = future1.get(); // 阻塞等待
String result2 = future2.get(); // 阻塞等待
// 好的做法:使用组合操作
CompletableFuture<String> combined = future1.thenCombine(future2, (r1, r2) -> r1 + r2);
异常传播
// 异常会在链中传播
CompletableFuture<String> chain = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("初始异常");
}).thenApply(result -> {
// 这里不会执行,因为前面抛出了异常
return result.toUpperCase();
}).exceptionally(ex -> {
// 这里会捕获初始异常
return "异常处理结果";
});
内存泄漏风险
// 避免创建过多的 CompletableFuture 对象
// 特别是在循环中创建时要注意内存使用
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(CompletableFuture.supplyAsync(() -> "任务" + i));
}
// 及时处理完成的 future
CompletableFuture<Void> allDone = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);