如何将参数传递给Java线程?


290

谁能建议我如何将参数传递给线程?

另外,它如何用于匿名类?


5
您介意为此添加一些额外的解释吗?有很多技术可以使用,但是所有技术都取决于最终目标。
Artem Barger 2009年

4
您的意思是将参数传递给已经运行的线程吗?因为当前所有答案都是关于将参数传递给新线程...
Valentin Rocher,2009年

现在您可以使用了Consumer<T>
Alex78191 '19

Answers:


371

您需要将构造函数中的参数传递给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
@jasonm不,它是一个构造函数-构造函数没有返回类型。
Alnitak 2012年

这意味着使用构造的每个线程r将具有相同的参数,因此,如果我们要将不同的参数传递给正在运行的多个线程,则需要为每个线程使用所需的参数MyThread创建一个新MyThread实例。换句话说,要启动和运行线程,我们需要创建两个对象:Thread和MyThread。从性能角度来看,这被认为是不好的吗?
艾萨克·克莱曼

2
@IsaacKleinman好,除非您扩展Thread,否则您还是必须这样做。在这种情况下,此解决方案仍然有效-只需将“ implements Runnable”更改为“ extends Thread”,将“ Runnable”更改为“ Thread”,将“ new Thread(r)”更改为“ r”。
user253751

111

对于匿名类:

在回应问题编辑时,这是匿名类的工作方式

   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();

Runnable是比Thread BTW更好的解决方案。所以我更喜欢:

   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();

这个答案与这个类似的问题基本相同:如何将参数传递给Thread对象


2
我有类似于您的匿名类示例的代码,除了我parameter直接从run()方法访问而p根本不使用任何字段。似乎有效。是否有一些我没有事先复制parameter到的微妙的多线程东西p
兰德尔·库克

我想你错过了)在第一个例子
哈克-R

我与@RandallCook参加匿名课程的经历相同。只要final X parameternew Runnable()生产线前有,我就可以进入parameter内部run()。我不需要做额外的事情p = parameter
威斯巴基

final不再那么重要了;变量有效地为最终变量就足够了(尽管使用它没有害处)
user85421

43

通过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();
}

5
+1用于显示如何扩展线程而不是实现Runnable。
Caelum 2014年

1
我们为什么需要@Override
下雪

@Snow @Override明确声明它正在覆盖Thread类中的抽象方法。
wyskoj

22

这个答案来得很晚,但是也许有人会觉得有用。它是关于如何在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;
    {
        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

17

创建线程时,您需要一个实例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");

当然,如果要在更改参数时触发操作,则必须使用锁,这会使事情变得更加复杂。


1
添加二传手会不会创造潜在的比赛条件?如果线程以变量作为一个值开头,但是设置器在执行过程中对其进行了更改,那不会有问题吗?
anon58192932 '10 -10-12

是的,设置器和对参数的所有访问都应该同步。
jwoolard

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);
   }
}

8

您可以扩展或并根据需要提供参数。在docs中有简单的示例。我将它们移植到这里:Thread classRunnable class

 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();

7

编写一个实现Runnable的类,并在适当定义的构造函数中传递所需的任何内容,或者编写一个使用适当定义的构造函数扩展Thread的类,该构造函数调用具有适当参数的super()。


6

从Java 8开始,您可以使用lambda捕获有效为final的参数。例如:

final String param1 = "First param";
final int param2 = 2;
new Thread(() -> {
    // Do whatever you want here: param1 and param2 are in-scope!
    System.out.println(param1);
    System.out.println(param2);
}).start();

5

在Java 8中,您可以将lambda表达式与Concurrency API配合使用,并ExecutorService作为高级替换直接使用线程:

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

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

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

另请参见executors javadocs


最后是没有这些烦人的领域的一种方法。现在我只需要等待我的产品升级到Java 8
斯里达尔Sarnobat

5

我知道我迟到了几年,但是我遇到了这个问题并采取了非传统的方法。我想不上新课就去做,所以我想到了:

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();

4

通过start()和run()方法传递的参数:

// Tester
public static void main(String... args) throws Exception {
    ThreadType2 t = new ThreadType2(new RunnableType2(){
        public void run(Object object) {
            System.out.println("Parameter="+object);
        }});
    t.start("the parameter");
}

// New class 1 of 2
public class ThreadType2 {
    final private Thread thread;
    private Object objectIn = null;
    ThreadType2(final RunnableType2 runnableType2) {
        thread = new Thread(new Runnable() {
            public void run() {
                runnableType2.run(objectIn);
            }});
    }
    public void start(final Object object) {
        this.objectIn = object;
        thread.start();
    }
    // If you want to do things like setDaemon(true); 
    public Thread getThread() {
        return thread;
    }
}

// New class 2 of 2
public interface RunnableType2 {
    public void run(Object object);
}

3

您可以从Runnable派生一个类,并在构造过程中(例如)传递参数。

然后使用Thread.start(Runnable r)启动它;

如果您要线程运行时进行操作,则只需在调用线程中保留对派生对象的引用,然后调用适当的setter方法(在适当的地方进行同步)


2

有一种将参数传递到可运行对象的简单方法。码:

public void Function(final type variable) {
    Runnable runnable = new Runnable() {
        public void run() {
            //Code adding here...
        }
    };
    new Thread(runnable).start();
}

2

不,您不能将参数传递给run()方法。签名告诉您(它没有参数)。可能最简单的方法是使用一个专用对象,该对象在构造函数中使用参数并将其存储在最终变量中:

public class WorkingTask implements Runnable
{
    private final Object toWorkWith;

    public WorkingTask(Object workOnMe)
    {
        toWorkWith = workOnMe;
    }

    public void run()
    {
        //do work
    }
}

//...
Thread t = new Thread(new WorkingTask(theData));
t.start();

一旦这样做-您必须注意传递给'WorkingTask'的对象的数据完整性。数据现在将存在于两个不同的线程中,因此您必须确保它是线程安全的。


1

另一种选择;这种方法使您可以像异步函数调用一样使用Runnable项。如果您的任务不需要返回结果,例如,它仅执行某些操作,则无需担心如何传递“结果”。

这种模式使您可以重用需要某种内部状态的项目。当在构造函数中不传递参数时,需要注意调解程序对参数的访问。如果用例涉及不同的调用者,则可能需要更多检查。

public class MyRunnable implements Runnable 
{
  private final Boolean PARAMETER_LOCK  = false;
  private X parameter;

  public MyRunnable(X parameter) {
     this.parameter = parameter;
  }

  public void setParameter( final X newParameter ){

      boolean done = false;
      synchronize( PARAMETER_LOCK )
      {
          if( null == parameter )
          {
              parameter = newParameter;
              done = true;
          }
      }
      if( ! done )
      {
          throw new RuntimeException("MyRunnable - Parameter not cleared." );
      }
  }


  public void clearParameter(){

      synchronize( PARAMETER_LOCK )
      {
          parameter = null;
      }
  }


  public void run() {

      X localParameter;

      synchronize( PARAMETER_LOCK )
      {
          localParameter = parameter;
      }

      if( null != localParameter )
      {
         clearParameter();   //-- could clear now, or later, or not at all ...
         doSomeStuff( localParameter );
      }

  }

}

线程t = new Thread(new MyRunnable(parameter)); t.start();

如果需要处理结果,则还需要在子任务完成时协调MyRunnable的完成。您可以回拨电话,也可以等待线程“ t”等。


1

专为Android

为了回调,我通常Runnable使用输入参数实现自己的泛型:

public interface Runnable<TResult> {
    void run(TResult result);
}

用法很简单:

myManager.doCallbackOperation(new Runnable<MyResult>() {
    @Override
    public void run(MyResult result) {
        // do something with the result
    }
});

在经理中:

public void doCallbackOperation(Runnable<MyResult> runnable) {
    new AsyncTask<Void, Void, MyResult>() {
        @Override
        protected MyResult doInBackground(Void... params) {
            // do background operation
            return new MyResult(); // return resulting object
        }

        @Override
        protected void onPostExecute(MyResult result) {
            // execute runnable passing the result when operation has finished
            runnable.run(result);
        }
    }.execute();
}

1

创建你的类,一个局部变量extends Threadimplements Runnable

public class Extractor extends Thread {
    public String webpage = "";
    public Extractor(String w){
        webpage = w;
    }
    public void setWebpage(String l){
        webpage = l;
    }

    @Override
    public void run() {// l is link
        System.out.println(webpage);
    }
    public String toString(){
        return "Page: "+webpage;
    }}

这样,您可以在运行变量时传递它。

Extractor e = new Extractor("www.google.com");
e.start();

输出:

"www.google.com"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.