Jak mogę przekazać parametr do wątku Java?


290

Czy ktoś może mi zasugerować, jak mogę przekazać parametr do wątku?

Jak to działa w przypadku anonimowych zajęć?


5
Czy mógłbyś dodać dodatkowe wyjaśnienie tego, co dokładnie próbujesz zrobić? Jest wiele technik, ale wszystkie zależą od ostatecznego celu.
Artem Barger,

4
Masz na myśli przekazanie parametru do już działającego wątku? Ponieważ wszystkie obecne odpowiedzi dotyczą przekazywania parametrów do nowych wątków ...
Valentin Rocher

Teraz możesz użyć Consumer<T>.
Alex78191,

Odpowiedzi:


371

Musisz przekazać parametr w konstruktorze do obiektu Runnable:

public class MyRunnable implements Runnable {

   public MyRunnable(Object parameter) {
       // store parameter for later user
   }

   public void run() {
   }
}

i wywołaj to w ten sposób:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();

7
@ jasonm nie, to konstruktor - konstruktory nie mają typu zwracanego.
Alnitak

Oznacza to, że każdy wątek zbudowany przy użyciu rbędzie miał ten sam argument, więc jeśli chcemy przekazać różne argumenty do wielu działających wątków MyThread, musimy utworzyć nową MyThreadinstancję, używając żądanego argumentu dla każdego wątku. Innymi słowy, aby uruchomić wątek, musimy utworzyć dwa obiekty: Thread i MyThread. Czy jest to uważane za złe pod względem wydajności?
Isaac Kleinman

2
@IsaacKleinman dobrze, i tak musisz to zrobić, chyba że rozszerzysz wątek. To rozwiązanie nadal działa w takim przypadku - wystarczy zmienić „implementuje Runnable” na „rozszerza wątek”, „Runnable” na „Thread” i „new Thread (r)” na „r”.
user253751

111

Dla klas anonimowych:

W odpowiedzi na edycję pytania o to, jak to działa dla klas Anonimowych

   final X parameter = ...; // the final is important
   Thread t = new Thread(new Runnable() {
       p = parameter;
       public void run() { 
         ...
       };
   t.start();

Nazwane klasy:

Masz klasę, która rozszerza wątek (lub implementuje Runnable) oraz konstruktor o parametrach, które chcesz przekazać. Następnie, gdy tworzysz nowy wątek, musisz przekazać argumenty, a następnie rozpocząć wątek, mniej więcej tak:

Thread t = new MyThread(args...);
t.start();

Runnable jest znacznie lepszym rozwiązaniem niż Thread BTW. Wolałbym więc:

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

Ta odpowiedź jest w zasadzie taka sama jak w przypadku podobnego pytania: Jak przekazać parametry do obiektu Thread


2
Mam kod podobny do twojego anonimowego przykładu klasy, z tym wyjątkiem, że uzyskuję dostęp parameterbezpośrednio z run()metody bez użycia pola jak pw ogóle. Wydaje się, że działa. Czy brakuje mi subtelnej wielowątkowości, parameterktórej pwcześniej nie kopiowałem ?
Randall Cook

Myślę, że brakuje )ci pierwszego przykładu
Hack-R

Miałem takie same doświadczenia jak @RandallCook for Anonymous class. Tak długo, jak miałem final X parameterprzed new Runnable()linią, miałem dostęp do parameterśrodka run(). Nie musiałem robić dodatkowych rzeczy p = parameter.
wisbucky

finalnie jest już tak ważny; wystarczy, jeśli zmienna jest faktycznie ostateczna (mimo że nie szkodzi jej posiadanie)
user85421

43

za pomocą konstruktora klasy Runnable lub 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 za pokazanie, jak to zrobić, rozszerzając Wątek zamiast implementować Runnable.
Caelum,

1
Dlaczego musimy @Overrideza?
Śnieg

@ Śnieg @Overridewyraźnie stwierdza, że ​​zastępuje metodę abstrakcyjną w klasie Thread.
wyskoj

22

Ta odpowiedź przychodzi bardzo późno, ale może ktoś uzna ją za przydatną. Chodzi o to, jak przekazać parametr (y) do Runnablenawet bez deklarowania nazwanej klasy (przydatne dla wkładek):

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

Oczywiście możesz przełożyć wykonanie startna bardziej dogodny lub odpowiedni moment. I to od Ciebie zależy, jaki będzie podpisinit metody (może więc wymagać więcej i / lub różnych argumentów) i oczywiście nawet jej nazwy, ale w zasadzie masz pomysł.

W rzeczywistości istnieje również inny sposób przekazania parametru do anonimowej klasy za pomocą bloków inicjalizujących. Rozważ to:

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

Wszystko dzieje się więc w bloku inicjalizacyjnym.


Ten ostatni przykład bardzo mi się podobał. Przypomina mi użycie zamknięcia w JS do odwoływania się do zmiennych w zakresie zewnętrznym. Ale czy this.myParamwtedy naprawdę jest to konieczne? Czy nie można po prostu porzucić zmiennych prywatnych i odwołać się do zmiennej z zakresu zewnętrznego? Rozumiem (oczywiście), że ma to pewne implikacje, takie jak że zmienna jest otwarta na zmianę po uruchomieniu wątku.
oligofren

@JeffG faktycznie odpowiedział na to w odpowiedzi poniżej!
oligofren

17

Podczas tworzenia wątku potrzebujesz wystąpienia Runnable. Najprostszym sposobem na przekazanie parametru byłoby przekazanie go jako argumentu do konstruktora:

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

Jeśli następnie chcesz zmienić parametr podczas działania wątku, możesz po prostu dodać metodę ustawiającą do klasy uruchamialnej:

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

Gdy to zrobisz, możesz zmienić wartość parametru, wywołując w ten sposób:

myRunnable.setMyParam("Goodbye World");

Oczywiście, jeśli chcesz wywołać akcję po zmianie parametru, będziesz musiał użyć blokad, co znacznie komplikuje sytuację.


1
Czy dodanie setera nie stwarza potencjalnych warunków wyścigu? Jeśli wątek zaczyna się od zmiennej jako jednej wartości, ale ustawiający ją zmienia w połowie wykonania, czy nie byłoby to problematyczne?
anon58192932,

To prawda, że ​​ustawiający i cały dostęp do parametru powinny być zsynchronizowane.
jwoolard,

9

Aby utworzyć wątek, zwykle tworzysz własną implementację Runnable. Przekaż parametry do wątku w konstruktorze tej klasy.

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

Możesz rozszerzyć lub i podać parametry, jak chcesz. Istnieją proste przykłady w docs . Przeniesię je tutaj: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

Napisz klasę, która implementuje Runnable, i przekaż wszystko, czego potrzebujesz w odpowiednio zdefiniowanym konstruktorze, lub napisz klasę, która rozszerza Thread o odpowiednio zdefiniowanym konstruktorze, który wywołuje super () z odpowiednimi parametrami.


6

Począwszy od Java 8, możesz użyć lambda do przechwytywania parametrów, które są faktycznie ostateczne . Na przykład:

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

W Javie 8 możesz używać lambdawyrażeń z interfejsem API współbieżności i ExecutorServicejako zamiennik wyższego poziomu do bezpośredniej pracy z wątkami:

newCachedThreadPool()Tworzy pulę wątków, która w razie potrzeby tworzy nowe wątki, ale użyje wcześniej zbudowanych wątków, gdy będą one dostępne. Pule te zazwyczaj poprawiają wydajność programów wykonujących wiele krótkotrwałych zadań asynchronicznych.

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

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

Zobacz także executors javadocs .


Wreszcie sposób na zrobienie tego bez tych irytujących pól. Teraz muszę tylko poczekać, aż mój produkt zaktualizuje się do wersji Java 8.
Sridhar Sarnobat,

5

Wiem, że jestem spóźniony o kilka lat, ale natknąłem się na ten problem i podjąłem niekonwencjonalne podejście. Chciałem to zrobić bez tworzenia nowej klasy, więc oto co wymyśliłem:

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

Przekazywanie parametrów za pomocą metod start () i 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

Możesz wyprowadzić klasę z Runnable, a podczas budowy (powiedzmy) przekaż parametr w.

Następnie uruchom go za pomocą Thread.start (Runnable r);

Jeśli masz na myśli, gdy wątek jest uruchomiony, po prostu trzymaj odwołanie do swojego obiektu pochodnego w wątku wywołującym i wywołaj odpowiednie metody ustawiające (synchronizując w razie potrzeby)


2

Istnieje prosty sposób przekazywania parametrów do programów runnables. Kod:

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

2

Nie, nie możesz przekazać parametrów do run()metody. Podpis mówi ci to (nie ma parametrów). Prawdopodobnie najłatwiejszym sposobem na to byłoby użycie specjalnie zbudowanego obiektu, który pobiera parametr w konstruktorze i przechowuje go w zmiennej końcowej:

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

Gdy to zrobisz - musisz uważać na integralność danych obiektu, który przekazujesz do „WorkingTask”. Dane będą teraz istnieć w dwóch różnych wątkach, więc musisz upewnić się, że są bezpieczne.


1

Jeszcze jedna opcja; takie podejście pozwala używać elementu Runnable jako asynchronicznego wywołania funkcji. Jeśli twoje zadanie nie musi zwracać wyniku, np. Wykonuje tylko pewną czynność, nie musisz się martwić o to, jak przekazać „wynik”.

Ten wzór pozwala ponownie użyć przedmiotu, w którym potrzebujesz pewnego rodzaju stanu wewnętrznego. Gdy parametry nie są przekazywane do konstruktora, należy zachować ostrożność, aby pośredniczyć w dostępie programów do parametrów. Możesz potrzebować więcej kontroli, jeśli twój przypadek użycia dotyczy różnych rozmówców itp.

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

  }

}

Wątek t = nowy wątek (nowy MyRunnable (parametr)); t.start ();

Jeśli potrzebujesz wyniku przetwarzania, będziesz także musiał skoordynować ukończenie MyRunnable po zakończeniu zadania cząstkowego. Możesz oddzwonić lub poczekać na Wątek „t” itp.


1

Specjalnie dla Androida

Dla celów oddzwaniania zwykle implementuję swój własny rodzajowy Runnablez parametrami wejściowymi:

public interface Runnable<TResult> {
    void run(TResult result);
}

Użycie jest proste:

myManager.doCallbackOperation(new Runnable<MyResult>() {
    @Override
    public void run(MyResult result) {
        // do something with the result
    }
});

W menedżerze:

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

Utwórz zmienną lokalną w swojej klasie, która extends Threadlub 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;
    }}

W ten sposób możesz przekazać zmienną po uruchomieniu.

Extractor e = new Extractor("www.google.com");
e.start();

Wyjście:

"www.google.com"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.