Java 如何做异步
很多上班族应该都有过同时做几件事的经历:煮上粥的同时,收拾餐桌、准备上班用的文件,不用等粥煮好再做别的,这样效率高很多。写代码也是一样,如果一件事可以拆分出来慢慢做,不用等它结束再处理后续,就能节省不少时间。Java里的“异步”,就是帮代码实现这种“不用等”的工具,今天就用生活化的例子讲清楚它的用法。
最基础的异步:用线程分开做
刚学Java的人,最容易想到的异步方式就是开新线程。比如你要做两件事:下载一份大报告,同时整理桌面文件。如果用同步的写法,得等报告下载完,才能开始整理,总时间是“下载时间+整理时间”。但用异步的话,把下载报告的任务交给另一个线程,主线程直接去整理文件,总时间就变成“下载时间”和“整理时间”里更长的那个,效率一下就提上来了。

举个实际的代码小例子,用最原始的线程写法:
`java
// 主线程做整理文件的事
new Thread(() -> {
// 另一个线程做下载报告的事
System.out.println("开始下载报告,大概需要5秒");
try {
Thread.sleep(5000); // 模拟下载耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("报告下载完成,可以开始处理了");
}).start();
System.out.println("现在就去整理桌面文件,大概需要3秒");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("桌面整理好了");
`
运行这段代码,你会看到“整理桌面文件”先打印,“下载报告”后打印,不用等下载完再整理,这就是最基础的异步效果。不过这种方式有个小问题:如果要管理多个异步任务,比如同时下载3份报告,手动开线程就会很乱,还可能浪费资源。
更省心的异步:用CompletableFuture
后来Java出了CompletableFuture,就像给你配了个“任务小秘书”,能同时管理好几个异步任务,还能在任务完成后自动处理后续步骤,不用自己盯着。还是用刚才的例子,现在要同时下载报告和收集客户信息,等两个都完成后,再把内容合并起来。
用CompletableFuture的写法是这样的:
`java
// 异步下载报告
CompletableFuture<String> downloadReport = CompletableFuture.supplyAsync(() -> {
System.out.println("正在下载报告");
try { Thread.sleep(5000); } catch (InterruptedException e) {}
return "2024年度销售报告";
});
// 异步收集客户信息
CompletableFuture<String> getClientInfo = CompletableFuture.supplyAsync(() -> {
System.out.println("正在收集客户信息");
try { Thread.sleep(3000); } catch (InterruptedException e) {}
return "10位核心客户信息";
});
// 两个任务都完成后,自动合并内容
CompletableFuture<String> mergeTask = downloadReport.thenCombine(getClientInfo, (report, info) -> {
return report + " + " + info;
});
// 最终结果
System.out.println("合并后的内容:" + mergeTask.get());
`
这个写法里,“小秘书”会同时安排下载和收集,等两个都结束后自动合并,不用你手动等每个任务。要是其中一个任务出错,比如下载报告失败,它还能自动处理,不用整个流程卡住,比手动开线程省心多了。
专业场景的异步:线程池兜底
不管是手动开线程还是CompletableFuture,都需要用到“线程池”,它就像一个固定大小的员工队伍,不会每次都新招临时工(新开线程),避免资源浪费。比如你要同时处理10个异步任务,线程池就安排10个员工一起做,要是只有5个员工,就排队等,不会让电脑因为开太多线程变卡。
举个简单的线程池例子,给CompletableFuture配个线程池:
`java
// 创建一个固定有5个员工的线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
// 用这个线程池做异步任务
CompletableFuture.supplyAsync(() -> {
// 异步任务内容
}, pool);
`
这样就不用每次自己管线程的数量,专业场景里都会用线程池来管理异步任务,避免出问题。
总结一下,Java做异步没有什么复杂的黑科技,就是把“不用等的事”交给单独的线程处理,从最基础的手动开线程,到用CompletableFuture管理多个任务,再用线程池兜底,根据自己的需求选就行。小任务用简单的方式就够,多任务、复杂流程就用CompletableFuture,就能写出效率更高的代码。