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

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

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

【问题讨论】:

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

标签: java multithreading


【解决方案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 APIExecutorServicelambda 表达式作为直接使用线程的更高级别的替代:

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

                        【讨论】:

                        【解决方案19】:

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

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

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

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

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

                        【讨论】:

                          猜你喜欢
                          • 2014-01-05
                          • 2011-02-18
                          • 1970-01-01
                          • 1970-01-01
                          • 2015-03-08
                          • 2012-08-16
                          • 1970-01-01
                          相关资源
                          最近更新 更多