线程池管理任务提交方式的实用技巧

线程里的任务是怎么提交

在开发后台服务或者处理批量任务时,经常遇到需要并发执行多个操作的情况。比如,一个电商系统在大促期间要处理成千上万的订单通知发送。如果每个通知都新建一个线程,系统很快就会崩溃。这时候就得靠线程池管理,而任务提交方式就是关键。

直接用 execute 提交 Runnable 任务

最基础的方式是使用 execute() 方法,它接受一个 Runnable 对象。适合那些不需要返回结果的任务,比如记录日志、发送短信。

ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(() -> {
    System.out.println("正在发送通知:用户123");
});

用 submit 提交有返回值的任务

如果任务需要返回结果,比如查询用户积分、计算折扣价格,就得用 submit()。它可以接收 CallableRunnable,并返回一个 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();
}

线程池不是扔了任务就完事的工具。掌握不同的提交方式,能让程序更高效也更可控。就像快递站分拣包裹,方式对了,整个流程才不会堵。