线程池里的任务是怎么提交的
在开发后台服务或者处理批量任务时,经常遇到需要并发执行多个操作的情况。比如,一个电商系统在大促期间要处理成千上万的订单通知发送。如果每个通知都新建一个线程,系统很快就会崩溃。这时候就得靠线程池来管理,而任务提交方式就是关键。
直接用 execute 提交 Runnable 任务
最基础的方式是使用 execute() 方法,它接受一个 Runnable 对象。适合那些不需要返回结果的任务,比如记录日志、发送短信。
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(() -> {
System.out.println("正在发送通知:用户123");
});
用 submit 提交有返回值的任务
如果任务需要返回结果,比如查询用户积分、计算折扣价格,就得用 submit()。它可以接收 Callable 或 Runnable,并返回一个 Future 对象。
Future<Integer> future = pool.submit(() -> {
return 80 + 20; // 模拟计算
});
// 后续获取结果
int result = future.get(); // 阻塞直到完成
System.out.println("计算结果:" + result);
批量提交任务用 invokeAll 更省心
当你要同时跑一堆相似任务,比如给多个用户查余额,用 invokeAll() 一次性提交更方便。它会返回一组 Future,按顺序对应每个任务。
List<Callable<Integer>> tasks = new ArrayList<>();
tasks.add(() -> 100);
tasks.add(() -> 200);
tasks.add(() -> 150);
List<Future<Integer>> results = pool.invokeAll(tasks);
for (Future<Integer> res : results) {
System.out.println("拿到结果:" + res.get());
}
哪个任务先完就处理哪个:invokeAny
有时候你只关心最快完成的那个结果。比如向多个推荐服务发请求,谁先回来就用谁的数据。这时候 invokeAny() 就派上用场了,它会返回第一个成功完成的任务结果。
String result = pool.invokeAny(Arrays.asList(
() -> {
Thread.sleep(2000);
return "推荐服务A";
},
() -> {
Thread.sleep(1000);
return "推荐服务B";
}
));
System.out.println("采用结果:" + result); // 输出 推荐服务B
别忘了合理关闭线程池
任务提交完了,不代表线程池就能放着不管。如果不关闭,JVM 可能无法正常退出。建议在合适时机调用 shutdown(),然后等待任务结束。
pool.shutdown();
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // 超时后强制关闭
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
线程池不是扔了任务就完事的工具。掌握不同的提交方式,能让程序更高效也更可控。就像快递站分拣包裹,方式对了,整个流程才不会堵。