Як я можу передати параметр до потоку 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. І це рішення все ще працює в такому випадку - просто змініть "реалізує Runnable" на "розширює нитку", "Runnable" на "Thread", а "new Thread (r)" на "r".
користувач253751

111

Для анонімних занять:

У відповідь на редагування питань ось як це працює для занять Anonymous

   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 для Anonymous. Поки я мав 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();
}

5
+1 для показу, як це зробити, розширюючи нитку, а не реалізувати Runnable.
Caelum

1
для чого нам потрібні @Override?
Сніг

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

Так все відбувається всередині блоку ініціалізатора.


Мені насправді дуже сподобався цей останній приклад. Нагадує мені використовувати закриття в JS для посилання змінних у зовнішній області. Але чи this.myParamдійсно тоді потрібне? Не могли ви просто скинути приватні змінні та звернутися до змінної із зовнішньої області застосування? Я розумію (звичайно), що це має певні наслідки, такі як змінна, відкрита для зміни після запуску потоку.
олігофрен

@JeffG насправді відповів на це у відповіді нижче!
олігофрен

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

Якщо ви хочете змінити параметр під час запуску потоку, ви можете просто додати метод встановлення у свій клас запуску:

public void setMyParam(String value){
    this.myParam = value;
}

Після цього ви можете змінити значення параметра, зателефонувавши так:

myRunnable.setMyParam("Goodbye World");

Звичайно, якщо ви хочете запустити дію при зміні параметра, вам доведеться використовувати блокування, що робить речі значно складнішими.


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

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 () з відповідними параметрами.


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вирази з API Concurrency & the 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();

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

Якщо ви маєте на увазі, поки нитка запущена, просто утримуйте посилання на свій похідний об'єкт у викличній потоці та зателефонуйте у відповідні методи встановлення (синхронізуючи, де це можливо)


2

Існує простий спосіб передачі параметрів у бігучі. Код:

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

Як тільки ви це зробите - ви повинні бути обережними щодо цілісності даних об'єкта, який ви передаєте у "WorkingTask". Дані тепер існуватимуть у двох різних потоках, тому ви повинні переконатися, що це безпечно для потоків.


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

  }

}

Thread t = new Thread (новий 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"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.