如何向Java线程传递参数?

338

有人可以向我建议如何将参数传递给线程吗?

对于匿名类,它是如何工作的呢?


6
你介意对你所要做的事情进行一些额外的解释吗?有很多不同的技巧可以用来完成它,但所有这些技巧都取决于最终目标。 - Artem Barger
4
你的意思是向已经在运行的线程传递参数吗?因为所有当前的答案都是关于向新线程传递参数的... - Valentin Rocher
现在您可以使用 Consumer<T> - Alex78191
19个回答

421
你需要在构造函数中传递参数给Runnable对象:

你需要在构造函数中传递参数给Runnable对象:

public class MyRunnable implements Runnable {

   public MyRunnable(Object parameter) {
       // store parameter for later user
   }

   public void run() {
   }
}

并且这样调用它:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();

7
不,这是一个构造函数 - 构造函数没有返回类型。 - Alnitak
这意味着使用“r”构造的每个线程将具有相同的参数,因此如果我们想要将不同的参数传递给运行“MyThread”的多个线程,我们需要为每个线程使用所需的参数创建一个新的“MyThread”实例。换句话说,要启动和运行线程,我们需要创建两个对象:Thread 和 MyThread。从性能角度来看,这是否被认为是不好的? - Isaac Kleinman
2
@IsaacKleinman 嗯,无论如何你都必须这样做,除非你扩展Thread。即使在这种情况下,这个解决方案仍然有效 - 只需将“implements Runnable”更改为“extends Thread”,“Runnable”更改为“Thread”,并将“new Thread(r)”更改为“r”。 - user253751
那么这意味着Java通常不支持它? - Cflowe Visit

128

对于匿名类:

针对问题的编辑,以下是匿名类的工作原理:

   final X parameter = ...; // the final is important
   Thread t = new Thread(new Runnable() {
       p = parameter;
       public void run() { 
         ...
       };
   t.start();

命名类:

如果有一个扩展Thread(或实现Runnable)且具有您想要传递的参数的构造函数的类。然后,当创建新线程时,需要传入参数,然后启动线程,如下所示:

Thread t = new MyThread(args...);
t.start();

顺便说一下,与Thread相比,Runnable是一个更好的解决方案。所以我更喜欢:

   public class MyRunnable implements Runnable {
      private X parameter;
      public MyRunnable(X parameter) {
         this.parameter = parameter;
      }

      public void run() {
      }
   }
   Thread t = new Thread(new MyRunnable(parameter));
   t.start();

这个答案与这个类似的问题基本相同:如何向线程对象传递参数


2
我有类似于你的匿名类示例的代码,除了我直接从run()方法中访问parameter而不使用像p这样的字段。它似乎可以工作。如果我在此之前没有将parameter复制到p中,是否会错过一些微妙的多线程问题? - Randall Cook
我认为在第一个例子中你漏了一个 ) - Hack-R
我和@RandallCook在匿名类方面有着相同的经历。只要在new Runnable()行之前有final X参数,那么我就可以在run()内部访问parameter。我不需要额外做p = parameter - wisbucky
final 不再那么重要了;如果变量是有效的 final(尽管没有它也没有关系),那就足够了。 - user85421

57

通过一个Runnable或Thread类的构造函数

class MyThread extends Thread {

    private String to;

    public MyThread(String to) {
        this.to = to;
    }

    @Override
    public void run() {
        System.out.println("hello " + to);
    }
}

public static void main(String[] args) {
    new MyThread("world!").start();
}

11
展示如何使用继承Thread而不是实现Runnable的方式来完成这个任务,给你点赞。 - Caelum
1
为什么我们需要 @Override 呢? - Snow
@Snow,@Override 明确表示它正在覆盖 Thread 类中的抽象方法。 - wyskoj

34

这个答案来得非常晚,但也许有人会发现它有用。它是关于如何在不声明命名类的情况下将参数传递给Runnable的方法(对于内联器非常方便):

    String someValue = "Just a demo, really...";

    new Thread(new Runnable() {
        private String myParam;

        public Runnable init(String myParam) {
            this.myParam = myParam;
            return this;
        }

        @Override
        public void run() {
            System.out.println("This is called from another thread.");
            System.out.println(this.myParam);
        }
    }.init(someValue)).start();

当然,您可以将start的执行延迟到更方便或适当的时候。而且,init方法的签名是由您决定的(因此它可能需要更多和/或不同的参数),甚至名称也可以改变,但基本上您已经有了一个想法。

实际上,还有另一种通过初始化块将参数传递给匿名类的方式。请考虑以下内容:

    String someValue = "Another demo, no serious thing...";
    int anotherValue = 42;

    new Thread(new Runnable() {
        private String myParam;
        private int myOtherParam;
        // instance initializer
        {
            this.myParam = someValue;
            this.myOtherParam = anotherValue;
        }

        @Override
        public void run() {
            System.out.println("This comes from another thread.");
            System.out.println(this.myParam + ", " + this.myOtherParam);
        }
    }).start();
所以所有的操作都发生在初始化块内部。

我其实很喜欢这个最后的例子。它让我想起在JS中使用闭包来引用外部作用域中的变量。但是this.myParam真的必要吗?你不能只丢掉私有变量并从外部作用域引用变量吗?当然,我理解这会带来一些影响,比如在启动线程后变量可能会被更改。 - oligofren
@JeffG 实际上在下面的回答中已经回答了这个问题! - oligofren

18

创建线程时,需要一个 Runnable 实例。最简单的传递参数的方式是将其作为构造函数的参数传递:

public class MyRunnable implements Runnable {

    private volatile String myParam;

    public MyRunnable(String myParam){
        this.myParam = myParam;
        ...
    }

    public void run(){
        // do something with myParam here
        ...
    }

}

MyRunnable myRunnable = new myRunnable("Hello World");
new Thread(myRunnable).start();

如果您想在线程运行时更改参数,则可以简单地向可运行类中添加一个setter方法:

public void setMyParam(String value){
    this.myParam = value;
}

有了这个,你可以通过如下方式调用来更改参数的值:

myRunnable.setMyParam("Goodbye World");

当然,如果你想在参数改变时触发某个操作,你将需要使用锁定(locks),这会使得事情变得更加复杂。


1
添加setter会创建潜在的竞态条件吗?如果线程以一个值作为变量的起点,但setter在执行过程中更改它,那么这不会有问题吗? - fIwJlxSzApHEZIl
正确的做法是,对于该参数的setter和所有访问都应该进行同步。 - jwoolard

12

我知道我有点晚了,但我遇到了这个问题并采取了一种非正统的方式。我想不用创建一个新类来解决它,所以我想出了这个方法:

int x = 0;
new Thread((new Runnable() {
     int x;
     public void run() {
        // stuff with x and whatever else you want
     }
     public Runnable pass(int x) {
           this.x = x;
           return this;
     }
}).pass(x)).start();

但实际上,您创建了一个新类 - 它只是匿名类。 :-) 另一方面,这正是 https://dev59.com/H3NA5IYBdhLWcg3wpvtg#39214833 中解决方案1的完全相同的方法。 - Cromax

10
您可以通过扩展Thread类或Runnable类并根据需要提供参数。在文档中有简单的示例。我将把它们放在这里:
 class PrimeThread extends Thread {
     long minPrime;
     PrimeThread(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }

 PrimeThread p = new PrimeThread(143);
 p.start();

 class PrimeRun implements Runnable {
     long minPrime;
     PrimeRun(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }


 PrimeRun p = new PrimeRun(143);
 new Thread(p).start();

9

要创建一个线程,通常需要创建自己的Runnable实现。在这个类的构造函数中传递参数给线程。

class MyThread implements Runnable{
   private int a;
   private String b;
   private double c;

   public MyThread(int a, String b, double c){
      this.a = a;
      this.b = b;
      this.c = c;
   }

   public void run(){
      doSomething(a, b, c);
   }
}

7
要么编写一个实现了Runnable接口的类,并在适当定义的构造函数中传入所需参数,要么编写一个继承Thread类的类,并使用适当定义的构造函数调用super()方法并传入适当的参数。

7
在Java 8中,您可以使用lambda表达式与并发APIExecutorService以更高级别的方式替代直接使用线程:

newCachedThreadPool()创建一个线程池,根据需要创建新线程,但在可用时将重用先前构造的线程。这些池通常会提高执行许多短期异步任务的程序的性能。

    private static final ExecutorService executor = Executors.newCachedThreadPool();

    executor.submit(() -> {
        myFunction(myParam1, myParam2);
    });

另请参见 executors Java文档


1
最终有一种方法可以不使用那些烦人的字段来完成。现在我只需要等待我的产品升级到Java 8即可。 - Sridhar Sarnobat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接