Может кто-нибудь предложить мне, как я могу передать параметр в поток?
Кроме того, как это работает для анонимных классов?
Consumer<T>
.
Может кто-нибудь предложить мне, как я могу передать параметр в поток?
Кроме того, как это работает для анонимных классов?
Consumer<T>
.
Ответы:
Вам необходимо передать параметр в конструкторе объекту 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();
r
будет иметь один и тот же аргумент, поэтому, если мы хотим передать разные аргументы нескольким запущенным потокам, MyThread
нам нужно будет создать новый MyThread
экземпляр, используя требуемый аргумент для каждого потока. Другими словами, чтобы запустить поток, нам нужно создать два объекта: Thread и MyThread. Считается ли это плохим с точки зрения производительности?
В ответ на редактирование вопроса вот как это работает для анонимных классов
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
parameter
напрямую из run()
метода без использования поля, как p
на всех. Вроде работает. Есть некоторые тонкие многопоточность , что мне не хватает не копировать parameter
на p
заранее?
)
в первом примере
final X parameter
до new Runnable()
линии, то я мог получить доступ parameter
внутрь run()
. Мне не нужно было делать лишнее p = parameter
.
final
уже не так важно; достаточно, если переменная является окончательной (несмотря на то, что она не причиняет вреда)
через конструктор класса 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();
}
@Override
?
@Override
явно заявляет, что переопределяет абстрактный метод в классе Thread.
Этот ответ приходит очень поздно, но, возможно, кто-то найдет его полезным. Речь идет о том, как передать параметр (ы) в 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();
Так что все происходит внутри блока инициализатора.
this.myParam
тогда действительно ли это необходимо? Не могли бы вы просто отбросить приватные переменные и обратиться к переменной из внешней области видимости? Я понимаю (конечно), что это имеет некоторые последствия, такие как переменная, открытая для изменения после запуска потока.
Когда вы создаете поток, вам нужен экземпляр 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 в свой класс runnable:
public void setMyParam(String value){
this.myParam = value;
}
Если у вас есть это, вы можете изменить значение параметра, вызвав так:
myRunnable.setMyParam("Goodbye World");
Конечно, если вы хотите инициировать действие при изменении параметра, вам придется использовать блокировки, которые значительно усложняют ситуацию.
Чтобы создать поток, вы обычно создаете собственную реализацию 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);
}
}
Вы можете расширить или предоставить и предоставить параметры по своему усмотрению. Есть простые примеры в документации . Я перенесу их сюда:Thread
class
Runnable
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();
Либо напишите класс, который реализует Runnable, и передайте все, что вам нужно, в соответствующем определенном конструкторе, или напишите класс, который расширяет Thread соответствующим образом определенным конструктором, который вызывает super () с соответствующими параметрами.
Начиная с Java 8, вы можете использовать лямбду для захвата параметров, которые являются окончательными . Например:
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();
В Java 8 вы можете использовать lambda
выражения с Concurrency API и ExecutorService
заменой более высокого уровня для работы с потоками напрямую:
newCachedThreadPool()
Создает пул потоков, который создает новые потоки по мере необходимости, но будет повторно использовать ранее созданные потоки, когда они станут доступны. Эти пулы обычно улучшают производительность программ, которые выполняют много кратковременных асинхронных задач.
private static final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> {
myFunction(myParam1, myParam2);
});
Смотрите также executors
Javadocs .
Я знаю, что опоздал на несколько лет, но я столкнулся с этой проблемой и принял неортодоксальный подход. Я хотел сделать это без создания нового класса, так что вот что я придумал:
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();
Передача параметров через методы 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);
}
Вы можете получить класс из Runnable и во время построения (скажем) передать параметр в.
Затем запустите его с помощью Thread.start (Runnable r);
Если вы имеете в виду во время работы потока, просто сохраните ссылку на ваш производный объект в вызывающем потоке и вызовите соответствующие методы установки (синхронизация, где это необходимо)
Нет, вы не можете передать параметры в 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();
Как только вы это сделаете - вы должны быть осторожны с целостностью данных объекта, который вы передаете в «Рабочую задачу». Теперь данные будут существовать в двух разных потоках, поэтому вы должны убедиться, что они безопасны для потоков.
Еще один вариант; этот подход позволяет использовать элемент 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 = новый поток (новый MyRunnable (параметр)); t.start ();
Если вам нужен результат обработки, вам также нужно будет координировать завершение MyRunnable после завершения подзадачи. Вы можете перезвонить или просто подождать в потоке 't' и т. Д.
Для целей обратного вызова я обычно реализую свой собственный шаблон 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();
}
Создайте локальную переменную в вашем классе, что 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"