1. 概述
在本教程中,我们将探讨如何向 Java 线程传递参数的不同方式。
2. 线程基础知识回顾
快速回顾一下,我们可以通过实现 Runnable
或 Callable
接口来创建一个线程。
要运行线程,可以调用 Thread#start
方法(传入 Runnable
实例),或者将任务提交给 ExecutorService
来使用线程池执行。
⚠️ 但无论是哪种方式,它们本身都不支持直接传参。
那么,我们有哪些办法来实现参数传递呢?
3. 通过构造函数传参
最简单粗暴的方式就是在构造函数中把参数传给 Runnable
或 Callable
实现类。
我们来写一个 AverageCalculator
类,它接收一组数字并计算平均值:
public class AverageCalculator implements Callable<Double> {
int[] numbers;
public AverageCalculator(int... numbers) {
this.numbers = numbers == null ? new int[0] : numbers;
}
@Override
public Double call() throws Exception {
return IntStream.of(numbers).average().orElse(0d);
}
}
然后我们提交这个任务到线程池,并验证结果:
@Test
public void whenSendingParameterToCallable_thenSuccessful() throws Exception {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Double> result = executorService.submit(new AverageCalculator(1, 2, 3));
try {
assertEquals(2.0, result.get().doubleValue());
} finally {
executorService.shutdown();
}
}
✅ 这种方式之所以有效,是因为我们在启动线程之前就已经把状态注入进去了。
4. 通过闭包(Closure)传参
另一种传参方式是利用闭包,这在使用 Lambda 表达式或匿名内部类时很常见。
我们扩展一下前面的例子,创建两个线程:
- 第一个计算总和:
executorService.submit(() -> IntStream.of(numbers).sum());
- 第二个计算平均值:
executorService.submit(() -> IntStream.of(numbers).average().orElse(0d));
来看一下完整示例:
@Test
public void whenParametersToThreadWithLamda_thenParametersPassedCorrectly()
throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
int[] numbers = new int[] { 4, 5, 6 };
try {
Future<Integer> sumResult =
executorService.submit(() -> IntStream.of(numbers).sum());
Future<Double> averageResult =
executorService.submit(() -> IntStream.of(numbers).average().orElse(0d));
assertEquals(Integer.valueOf(15), sumResult.get());
assertEquals(Double.valueOf(5.0), averageResult.get());
} finally {
executorService.shutdown();
}
}
⚠️ 注意:传入的变量必须是 effectively final,否则编译不通过。
⚠️ 同样地,多线程下的并发安全规则依然适用。如果在线程运行过程中修改了 numbers
数组的内容,而又没有加同步机制,那线程可能看不到最新值。
如果你还在用老版本 Java,也可以用匿名内部类来实现:
final int[] numbers = { 1, 2, 3 };
Thread parameterizedThread = new Thread(new Callable<Double>() {
@Override
public Double call() {
return calculateTheAverage(numbers);
}
});
parameterizedThread.start();
5. 总结
在这篇文章中,我们介绍了几种向 Java 线程传递参数的方法:
- ✅ 构造函数传参:最直观、最稳妥
- ✅ 闭包传参:适合 Lambda 和匿名类场景,但需注意变量必须是 effectively final
代码示例可以在 GitHub 仓库 获取。