如何将参数传递给 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
class
或 Runnable
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 和 ExecutorService
的 lambda
表达式作为直接使用线程的更高级别的替代:
newCachedThreadPool()
创建线程池,创建新线程 根据需要,但将重用以前构造的线程 可用的。这些池通常会提高执行许多短期异步任务的程序的性能。
private static final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() ->
myFunction(myParam1, myParam2);
);
另见executors
javadocs。
【讨论】:
终于找到了一种没有那些烦人的字段的方法。现在我只需要等待我的产品升级到 Java 8。【参考方案17】:在您的类中创建一个局部变量 extends Thread
或 implements 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 线程?的主要内容,如果未能解决你的问题,请参考以下文章