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

Posted

技术标签:

【中文标题】如何将参数传递给 Java 线程?【英文标题】:How can I pass a parameter to a Java Thread? 【发布时间】:2010-10-26 23:59:57 【问题描述】:

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

另外,匿名类是如何工作的?

【问题讨论】:

您是否介意添加一些额外的解释来说明您正在尝试做的事情?它有很多几种技术,但它们都取决于最终目标。 您的意思是将参数传递给已经运行的线程吗?因为目前所有的答案都是关于将参数传递给新线程...... 现在你可以使用Consumer<T>了。 【参考方案1】:

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

【讨论】:

+1 用于展示如何扩展 Thread 而不是实现 Runnable。 为什么我们需要@Override @Snow @Override 明确声明它正在覆盖 Thread 类中的抽象方法。【参考方案2】:

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

【讨论】:

【参考方案3】:

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

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

如果您的意思是同时线程正在运行,那么只需在调用线程中保存对派生对象的引用,并调用适当的 setter 方法(在适当的情况下同步)

【讨论】:

【参考方案4】:

需要将构造函数中的参数传递给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();

【讨论】:

@jasonm 不,它是一个构造函数——构造函数没有返回类型。 这意味着使用r构造的每个线程都将具有相同的参数,因此如果我们想将不同的参数传递给运行MyThread的多个线程,我们需要创建一个新的MyThread为每个线程使用所需参数的实例。换句话说,要让线程启动并运行,我们需要创建两个对象:Thread 和 MyThread。这在性能方面被认为是糟糕的吗? @IsaacKleinman 好吧,无论如何你都必须这样做,除非你扩展 Thread。在这种情况下,这个解决方案仍然有效——只需将“implements Runnable”改为“extends Thread”,“Runnable”改为“Thread”,“new Thread(r)”改为“r”。【参考方案5】:

对于匿名类:

针对问题编辑,这里是匿名类的工作原理

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

这个答案和这个类似的问题基本相同:How to pass parameters to a Thread object

【讨论】:

我的代码类似于您的匿名类示例,除了我直接从run() 方法访问parameter,根本不使用p 之类的字段。它似乎工作。由于没有事先将parameter 复制到p,我是否遗漏了一些微妙的多线程? 我认为您在第一个示例中缺少) 我和@RandallCook 在匿名课上的经历相同。只要我在new Runnable() 行之前有final X parameter,那么我就可以在run() 中访问parameter。我不需要做额外的p = parameter final 不再那么重要了;如果变量实际上是最终的就足够了(尽管拥有它并没有什么害处)【参考方案6】:

要创建线程,您通常会创建自己的 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 的实例。传递参数最简单的方法是将其作为参数传递给构造函数:

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

当然,如果你想在参数改变时触发一个动作,你将不得不使用锁,这使得事情变得相当复杂。

【讨论】:

添加 setter 不会产生潜在的竞争条件吗?如果线程以变量作为一个值开始,但 setter 在执行过程中更改了它,那会不会有问题? 没错,setter 和所有对参数的访问应该是同步的。【参考方案8】:

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

 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】:

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

【讨论】:

【参考方案10】:

另一种选择;这种方法让您可以像使用异步函数调用一样使用 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”等。

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

不,您不能将参数传递给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”的对象的数据完整性。数据现在将存在于两个不同的线程中,因此您必须确保它是线程安全的。

【讨论】:

【参考方案14】:

这个答案来得很晚,但也许有人会觉得它有用。它是关于如何在不声明命名类的情况下将参数传递给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 真的有必要吗?您不能只删除私有变量并从外部范围引用变量吗?我知道(当然)这有一些含义,例如在启动线程后变量被打开以更改。 @JeffG 实际上在下面的答案中回答了这个问题!【参考方案15】:

从 Java 8 开始,您可以使用 lambda 来捕获 effectively 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();

【讨论】:

【参考方案16】:

在 Java 8 中,您可以使用带有 Concurrency API 和 ExecutorServicelambda 表达式作为直接使用线程的更高级别的替代:

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

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

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

另见executorsjavadocs。

【讨论】:

终于找到了一种没有那些烦人的字段的方法。现在我只需要等待我的产品升级到 Java 8。【参考方案17】:

在您的类中创建一个局部变量 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"

【讨论】:

【参考方案18】:

我知道我晚了几年,但我遇到了这个问题并采取了非正统的方法。我想在不开设新课程的情况下做到这一点,所以这就是我想出的:

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

【讨论】:

但你实际上创建了一个新类——它只是一个匿名类。 :-) 另一方面,它与***.com/a/39214833/2024692中的解决方案1中的方法完全相同【参考方案19】:

首先我想指出其他答案是正确的。 但是,在构造函数中使用参数可能不是你们所有人的最佳选择。

在许多情况下,您会想要使用“匿名内部类”,并覆盖 run() 方法,因为为每次使用定义特定的类是很痛苦的。 (new MyRunnable()...)

并且在您创建 Runnable 时,您可能无法使用该参数在构造函数中传递它。例如,如果您将此对象传递给一个方法,该方法将在单独的线程中执行一些工作,然后调用您的可运行对象,并将该工作的结果应用到它。

在这种情况下,使用这样的方法: public MyRunnable withParameter(Object parameter),可能会成为更有用的选择。

我并不是说这是解决问题的最佳方法,但它会完成工作。

【讨论】:

以上是关于如何将参数传递给 Java 线程?的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何将参数传递给线程作业(回调)

如何将多个重复的参数传递给 CUDA 内核

java中,如何用POST方法将参数传递给第三方网站

有没有办法将参数传递给Runnable? [复制]

如何将参数传递给 Apache Apex 中的 application.java 类?

如何将参数传递给 Thread 中的 ThreadStart 方法?