Как я могу передать параметр в поток Java?

290

Может кто-нибудь предложить мне, как я могу передать параметр в поток?

Кроме того, как это работает для анонимных классов?

Стив
источник
5
Не могли бы вы добавить дополнительное объяснение того, что именно вы пытаетесь сделать? Есть много методов, но все они зависят от конечной цели.
Артем Баргер
4
Вы имеете в виду передачу параметра в уже запущенный поток? Потому что все текущие ответы о передаче параметров в новые темы ...
Валентин Роше
Теперь вы можете использовать Consumer<T>.
Alex78191

Ответы:

371

Вам необходимо передать параметр в конструкторе объекту 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();
Альнитак
источник
7
@jasonm нет, это конструктор - конструкторы не имеют возвращаемого типа.
Альнитак
Это означает, что каждый поток, созданный с использованием, rбудет иметь один и тот же аргумент, поэтому, если мы хотим передать разные аргументы нескольким запущенным потокам, MyThreadнам нужно будет создать новый MyThreadэкземпляр, используя требуемый аргумент для каждого потока. Другими словами, чтобы запустить поток, нам нужно создать два объекта: Thread и MyThread. Считается ли это плохим с точки зрения производительности?
Исаак Кляйнман
2
@IsaacKleinman, ты все равно должен это делать, если только не расширяешь Thread. И это решение по-прежнему работает в этом случае - просто измените «Implements Runnable» на «extends Thread», «Runnable» на «Thread» и «new Thread (r)» на «r».
user253751
111

Для анонимных классов:

В ответ на редактирование вопроса вот как это работает для анонимных классов

   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

Ник Фортескью
источник
2
У меня есть код, похожий на ваш пример анонимного класса, за исключением того, что я получаю доступ parameterнапрямую из run()метода без использования поля, как pна всех. Вроде работает. Есть некоторые тонкие многопоточность , что мне не хватает не копировать parameterна pзаранее?
Рэндалл Кук
Я думаю, что вам не хватает )в первом примере
Hack-R
У меня был такой же опыт, как у @RandallCook для анонимного класса. Пока у меня были final X parameterдо new Runnable()линии, то я мог получить доступ parameterвнутрь run(). Мне не нужно было делать лишнее p = parameter.
Висбуки
finalуже не так важно; достаточно, если переменная является окончательной (несмотря на то, что она не причиняет вреда)
user85421
43

через конструктор класса 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();
}
DFA
источник
5
+1 за показ того, как сделать это, расширяя Thread вместо реализации Runnable.
Caelum
1
зачем нам нужен @Override?
Снег
@ Сноу @Overrideявно заявляет, что переопределяет абстрактный метод в классе Thread.
Wyskoj
22

Этот ответ приходит очень поздно, но, возможно, кто-то найдет его полезным. Речь идет о том, как передать параметр (ы) в 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();

Так что все происходит внутри блока инициализатора.

Cromax
источник
Мне действительно очень понравился этот последний пример. Напоминает мне об использовании замыкания в JS для ссылки на переменные во внешней области видимости. Но this.myParamтогда действительно ли это необходимо? Не могли бы вы просто отбросить приватные переменные и обратиться к переменной из внешней области видимости? Я понимаю (конечно), что это имеет некоторые последствия, такие как переменная, открытая для изменения после запуска потока.
oligofren
@JeffG фактически ответил на это в ответе ниже!
oligofren
17

Когда вы создаете поток, вам нужен экземпляр 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");

Конечно, если вы хотите инициировать действие при изменении параметра, вам придется использовать блокировки, которые значительно усложняют ситуацию.

jwoolard
источник
1
Не добавляет ли сеттер потенциальные условия гонки? Если поток начинается с переменной как одного значения, но установщик меняет ее в середине выполнения, не будет ли это проблематичным?
anon58192932
Правда, сеттер и весь доступ к параметру должны быть синхронизированы.
Jwoolard
9

Чтобы создать поток, вы обычно создаете собственную реализацию 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);
   }
}
Mnementh
источник
8

Вы можете расширить или предоставить и предоставить параметры по своему усмотрению. Есть простые примеры в документации . Я перенесу их сюда:Thread classRunnable 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();
Бруно Конде
источник
7

Либо напишите класс, который реализует Runnable, и передайте все, что вам нужно, в соответствующем определенном конструкторе, или напишите класс, который расширяет Thread соответствующим образом определенным конструктором, который вызывает super () с соответствующими параметрами.

PaulJWilliams
источник
6

Начиная с 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();
Джефф Дж
источник
5

В Java 8 вы можете использовать lambdaвыражения с Concurrency API и ExecutorServiceзаменой более высокого уровня для работы с потоками напрямую:

newCachedThreadPool()Создает пул потоков, который создает новые потоки по мере необходимости, но будет повторно использовать ранее созданные потоки, когда они станут доступны. Эти пулы обычно улучшают производительность программ, которые выполняют много кратковременных асинхронных задач.

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

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

Смотрите также executors Javadocs .

Стюарт Кардалл
источник
Наконец, способ сделать это без этих раздражающих полей. Теперь мне нужно подождать, пока мой продукт обновится до Java 8.
Шридхар Сарнобат
5

Я знаю, что опоздал на несколько лет, но я столкнулся с этой проблемой и принял неортодоксальный подход. Я хотел сделать это без создания нового класса, так что вот что я придумал:

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();
meyi
источник
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);
}
Java42
источник
3

Вы можете получить класс из Runnable и во время построения (скажем) передать параметр в.

Затем запустите его с помощью Thread.start (Runnable r);

Если вы имеете в виду во время работы потока, просто сохраните ссылку на ваш производный объект в вызывающем потоке и вызовите соответствующие методы установки (синхронизация, где это необходимо)

Брайан Агнью
источник
2

Существует простой способ передачи параметров в runnables. Код:

public void Function(final type variable) {
    Runnable runnable = new Runnable() {
        public void run() {
            //Code adding here...
        }
    };
    new Thread(runnable).start();
}
хром
источник
2

Нет, вы не можете передать параметры в 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();

Как только вы это сделаете - вы должны быть осторожны с целостностью данных объекта, который вы передаете в «Рабочую задачу». Теперь данные будут существовать в двух разных потоках, поэтому вы должны убедиться, что они безопасны для потоков.

Михир Патель
источник
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 = новый поток (новый MyRunnable (параметр)); t.start ();

Если вам нужен результат обработки, вам также нужно будет координировать завершение MyRunnable после завершения подзадачи. Вы можете перезвонить или просто подождать в потоке 't' и т. Д.

воля
источник
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();
}
Андрей
источник
1

Создайте локальную переменную в вашем классе, что 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"
Calvin K
источник