如何向Java线程传递参数?

338

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

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


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

7

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

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

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

不,你不能向run()方法传递参数。方法签名告诉你这一点(它没有参数)。可能最简单的方法是使用一个专门构建的对象,在构造函数中接受参数并将其存储在final变量中:

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


2

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

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

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
在您的类中创建一个本地变量,该类应扩展Thread实现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"

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

0

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

在许多情况下,您将希望使用“匿名内部类”,并覆盖run()方法,因为为每个用途定义特定的类很繁琐。(new MyRunnable(){...}

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

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

我不认为这是解决问题的最佳方案,但它可以完成工作。


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