如何在Java线程池中运行多个Runnable?如何用ExecutorService并行执行多任务?
在Java线程池中运行多个Runnable实例与并行执行任务的方法
嘿,这个问题在Java并发开发里太常见了,我来一步步给你捋清楚怎么操作,保证你看完就能上手!
一、在线程池中运行多个Runnable实例
Java的ExecutorService是线程池的核心API,只要掌握它的基本用法,运行多个Runnable实例就非常简单。
步骤拆解
- 创建线程池实例:用
Executors工具类的工厂方法快速创建(也可以自定义ThreadPoolExecutor来更精细控制参数),比如:newFixedThreadPool(int nThreads):固定大小的线程池,适合任务量稳定的场景newCachedThreadPool():可缓存的线程池,空闲线程会被回收,适合短期大量任务的场景
- 定义多个Runnable任务:可以用Lambda表达式、匿名内部类或者实现Runnable接口的类来写任务逻辑
- 提交任务到线程池:调用
executor.submit(runnable)把任务丢进线程池,线程池会自动分配空闲线程执行 - 关闭线程池:任务提交完后一定要调用
shutdown(),否则JVM会因为线程池的非守护线程一直运行而无法退出
代码示例
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultiRunnablePoolDemo { public static void main(String[] args) { // 创建固定大小为3的线程池 ExecutorService executor = Executors.newFixedThreadPool(3); // 定义3个不同的Runnable任务 Runnable downloadTask = () -> { System.out.println("下载任务启动,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1500); // 模拟下载耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新标记中断状态 } System.out.println("下载任务完成"); }; Runnable parseTask = () -> { System.out.println("解析任务启动,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("解析任务完成"); }; Runnable saveTask = () -> { System.out.println("保存任务启动,线程:" + Thread.currentThread().getName()); try { Thread.sleep(800); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("保存任务完成"); }; // 提交所有任务 executor.submit(downloadTask); executor.submit(parseTask); executor.submit(saveTask); // 关闭线程池:停止接受新任务,等待已提交任务执行完毕 executor.shutdown(); } }
二、并行执行多个不同的方法或Runnable任务
核心思路其实和上面一致——把不同的方法或逻辑包装成可被线程池执行的任务(Runnable或Callable),然后提交即可。
1. 包装普通方法为Runnable(无返回值场景)
如果你的方法不需要返回结果,直接用方法引用或者Lambda把它包装成Runnable就行:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DifferentMethodsDemo { // 自定义方法A public static void generateReport() { System.out.println("生成报表中,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } // 自定义方法B public static void syncData() { System.out.println("同步数据中,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1800); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); // 用方法引用提交方法A executor.submit(DifferentMethodsDemo::generateReport); // 提交方法B executor.submit(DifferentMethodsDemo::syncData); // 直接提交匿名Runnable(适合临时逻辑) executor.submit(() -> { System.out.println("清理临时文件,线程:" + Thread.currentThread().getName()); try { Thread.sleep(900); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); executor.shutdown(); } }
2. 用Callable获取方法返回值(有返回值场景)
如果需要获取方法的执行结果,就用Callable接口代替Runnable,提交后会得到一个Future对象,通过它可以拿到返回值:
import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableWithResultDemo { // 有返回值的计算方法 public static Integer calculateAverage(int[] nums) { System.out.println("计算平均值中,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return -1; } int sum = 0; for (int num : nums) sum += num; return sum / nums.length; } // 有返回值的字符串处理方法 public static String formatString(String input) { System.out.println("格式化字符串中,线程:" + Thread.currentThread().getName()); try { Thread.sleep(1500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return "处理中断"; } return String.format("格式化后:%s", input.trim().toUpperCase()); } public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(2); // 创建Callable任务 Callable<Integer> avgTask = () -> calculateAverage(new int[]{10, 20, 30, 40}); Callable<String> formatTask = () -> formatString(" hello java concurrency "); // 提交任务并获取Future Future<Integer> avgFuture = executor.submit(avgTask); Future<String> formatFuture = executor.submit(formatTask); // 获取结果(会阻塞直到任务完成) System.out.println("平均值计算结果:" + avgFuture.get()); System.out.println("字符串格式化结果:" + formatFuture.get()); executor.shutdown(); } }
3. 批量提交并等待所有任务完成
如果有一组任务需要全部执行完再继续后续逻辑,可以用invokeAll()方法,它会阻塞直到所有任务执行完毕:
import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class BatchTaskDemo { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(3); // 批量创建Callable任务 List<Callable<String>> taskList = Arrays.asList( () -> { Thread.sleep(800); return "用户数据同步完成"; }, () -> { Thread.sleep(1200); return "订单数据同步完成"; }, () -> { Thread.sleep(1000); return "商品数据同步完成"; } ); // 提交所有任务并等待完成 List<Future<String>> futures = executor.invokeAll(taskList); // 遍历输出所有结果 for (Future<String> future : futures) { System.out.println(future.get()); } executor.shutdown(); } }
实用小提示
- 生产环境尽量避免用
Executors的默认工厂方法(比如newCachedThreadPool可能创建大量线程导致OOM),推荐直接用ThreadPoolExecutor构造函数,自定义核心线程数、最大线程数、任务队列大小等参数,更贴合业务场景。 - 处理
InterruptedException时,记得调用Thread.currentThread().interrupt()重新标记中断状态,让上层代码能感知到中断事件。 - 如果任务可能抛出异常,调用
Future.get()时要捕获ExecutionException,并处理任务内部的异常。
内容的提问来源于stack exchange,提问作者Vignesh
相关产品推荐
相关产品推荐

