1. 概述

在本教程中,我们将探讨如何向 Java 线程传递参数的不同方式。

2. 线程基础知识回顾

快速回顾一下,我们可以通过实现 RunnableCallable 接口来创建一个线程。

要运行线程,可以调用 Thread#start 方法(传入 Runnable 实例),或者将任务提交给 ExecutorService 来使用线程池执行。

⚠️ 但无论是哪种方式,它们本身都不支持直接传参。

那么,我们有哪些办法来实现参数传递呢?

3. 通过构造函数传参

最简单粗暴的方式就是在构造函数中把参数传给 RunnableCallable 实现类。

我们来写一个 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 仓库 获取。


原始标题:Passing Parameters to Java Threads