Wie kann ich einen Parameter an einen Java-Thread übergeben?

290

Kann mir jemand vorschlagen, wie ich einen Parameter an einen Thread übergeben kann?

Wie funktioniert es auch für anonyme Klassen?

Steve
quelle
5
Würde es Ihnen etwas ausmachen, eine zusätzliche Erklärung für genau das hinzuzufügen, was Sie versuchen zu tun? Es gibt viele verschiedene Techniken, aber alle hängen vom Endziel ab.
Artem Barger
4
Meinen Sie damit, einen Parameter an einen bereits laufenden Thread zu übergeben? Weil es bei allen aktuellen Antworten darum geht, Parameter an neue Threads zu übergeben ...
Valentin Rocher
Jetzt können Sie verwenden Consumer<T>.
Alex78191

Antworten:

371

Sie müssen den Parameter im Konstruktor an das Runnable-Objekt übergeben:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

und rufe es so auf:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();
Alnitak
quelle
7
@ jasonm nein, es ist ein Konstruktor - Konstruktoren haben keinen Rückgabetyp.
Alnitak
Dies bedeutet, dass jeder mit erstellte Thread rdasselbe Argument hat. Wenn wir also unterschiedliche Argumente an mehrere ausgeführte Threads übergeben möchten, MyThreadmüssen wir eine neue MyThreadInstanz mit dem gewünschten Argument für jeden Thread erstellen . Mit anderen Worten, um einen Thread zum Laufen zu bringen, müssen zwei Objekte erstellt werden: Thread und MyThread. Wird dies in Bezug auf die Leistung als schlecht angesehen?
Isaac Kleinman
2
@IsaacKleinman gut, das musst du sowieso tun, es sei denn, du erweiterst Thread. Und diese Lösung funktioniert in diesem Fall immer noch - ändern Sie einfach "implementiert Runnable" in "erweitert Thread", "Runnable" in "Thread" und "neuer Thread (r)" in "r".
user253751
111

Für anonyme Klassen:

Als Antwort auf Fragenbearbeitungen erfahren Sie hier, wie es für anonyme Klassen funktioniert

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

Benannte Klassen:

Sie haben eine Klasse, die Thread erweitert (oder Runnable implementiert), und einen Konstruktor mit den Parametern, die Sie übergeben möchten. Wenn Sie dann den neuen Thread erstellen, müssen Sie die Argumente übergeben und dann den Thread starten, ungefähr so:

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

Runnable ist eine viel bessere Lösung als Thread BTW. Also würde ich bevorzugen:

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

Diese Antwort entspricht im Wesentlichen der ähnlichen Frage: Übergeben von Parametern an ein Thread-Objekt

Nick Fortescue
quelle
2
Ich habe Code ähnlich Ihrem anonymen Klassenbeispiel, außer dass ich parameterdirekt von der run()Methode aus zugreife , ohne ein Feld wie dieses pzu verwenden. Es scheint zu funktionieren. Gibt es ein subtiles Multithreading , was ich vermisse von nicht kopieren vorher? parameterp
Randall Cook
Ich denke, Sie vermissen eine )im ersten Beispiel
Hack-R
Ich hatte die gleiche Erfahrung wie @RandallCook für die anonyme Klasse. Solange ich die final X parametervor der new Runnable()Leitung hatte, konnte ich parameterhineingehen run(). Ich musste das Extra nicht tun p = parameter.
wisbucky
finalist nicht mehr so ​​wichtig; Es ist genug, wenn die Variable effektiv endgültig ist (obwohl es keinen Schaden anrichtet)
user85421
43

über den Konstruktor einer Runnable- oder Thread-Klasse

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
quelle
5
+1, um zu zeigen, wie Thread erweitert wird, anstatt Runnable zu implementieren.
Caelum
1
warum brauchen wir das @Overridefür?
Schnee
@Snow the gibt @Overrideexplizit an, dass die abstrakte Methode in der Thread-Klasse überschrieben wird.
Wyskoj
22

Diese Antwort kommt sehr spät, aber vielleicht findet es jemand nützlich. Es geht darum, wie Sie einen oder mehrere Parameter an eine übergeben, Runnableohne eine benannte Klasse zu deklarieren (praktisch für Inliner):

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

Natürlich können Sie die Ausführung startauf einen günstigeren oder angemesseneren Zeitpunkt verschieben. Und es liegt an Ihnen, was die Signatur der initMethode sein wird (es kann also mehr und / oder andere Argumente erfordern) und natürlich sogar ihren Namen, aber im Grunde haben Sie eine Idee.

Tatsächlich gibt es auch eine andere Möglichkeit, einen Parameter unter Verwendung der Initialisierungsblöcke an eine anonyme Klasse zu übergeben. Bedenken Sie:

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

Alles geschieht also innerhalb des Initialisierungsblocks.

Cromax
quelle
Dieses letzte Beispiel hat mir sehr gut gefallen. Erinnert mich daran, einen Abschluss in JS zu verwenden, um Variablen im äußeren Bereich zu referenzieren. Aber ist das this.myParamdann wirklich notwendig? Könnten Sie nicht einfach die privaten Variablen löschen und auf die Variable aus dem äußeren Bereich verweisen? Ich verstehe (natürlich), dass dies einige Auswirkungen hat, z. B. dass die Variable nach dem Starten des Threads offen ist, um sich zu ändern.
Oligofren
@ JeffG hat dies tatsächlich in der Antwort unten beantwortet!
Oligofren
17

Wenn Sie einen Thread erstellen, benötigen Sie eine Instanz von Runnable. Der einfachste Weg, einen Parameter zu übergeben, besteht darin, ihn als Argument an den Konstruktor zu übergeben:

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

Wenn Sie den Parameter dann ändern möchten, während der Thread ausgeführt wird, können Sie Ihrer ausführbaren Klasse einfach eine Setter-Methode hinzufügen:

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

Sobald Sie dies haben, können Sie den Wert des Parameters ändern, indem Sie wie folgt aufrufen:

myRunnable.setMyParam("Goodbye World");

Wenn Sie beim Ändern des Parameters eine Aktion auslösen möchten, müssen Sie natürlich Sperren verwenden, was die Dinge erheblich komplexer macht.

Jwoolard
quelle
1
Schafft das Hinzufügen eines Setzers keine potenziellen Rennbedingungen? Wenn der Thread mit der Variablen als einem Wert beginnt, der Setter sie jedoch während der Ausführung ändert, wäre das nicht problematisch?
anon58192932
Richtig, der Setter und der gesamte Zugriff auf den Parameter sollten synchronisiert werden.
Jwoolard
9

Um einen Thread zu erstellen, erstellen Sie normalerweise Ihre eigene Implementierung von Runnable. Übergeben Sie die Parameter an den Thread im Konstruktor dieser Klasse.

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
quelle
8

Sie können entweder die oder die und erweitern und Parameter angeben, wie Sie möchten. Es gibt einfache Beispiele in den Dokumenten . Ich werde sie hier portieren: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();
bruno conde
quelle
7

Schreiben Sie entweder eine Klasse, die Runnable implementiert, und übergeben Sie alles, was Sie benötigen, in einem entsprechend definierten Konstruktor, oder schreiben Sie eine Klasse, die Thread um einen entsprechend definierten Konstruktor erweitert, der super () mit entsprechenden Parametern aufruft.

PaulJWilliams
quelle
6

Ab Java 8 können Sie ein Lambda verwenden, um Parameter zu erfassen, die effektiv endgültig sind . Zum Beispiel:

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();
Jeff G.
quelle
5

In Java 8 können Sie lambdaAusdrücke mit der Concurrency-API und dem ExecutorServiceals übergeordneten Ersatz für die direkte Arbeit mit Threads verwenden:

newCachedThreadPool()Erstellt einen Thread-Pool, der nach Bedarf neue Threads erstellt, zuvor erstellte Threads jedoch wiederverwendet, sobald sie verfügbar sind. Diese Pools verbessern normalerweise die Leistung von Programmen, die viele kurzlebige asynchrone Aufgaben ausführen.

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

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

Siehe auch executors Javadocs .

Stuart Cardall
quelle
Endlich ein Weg, es ohne diese nervigen Felder zu machen. Jetzt muss ich nur noch warten, bis mein Produkt auf Java 8 aktualisiert ist.
Sridhar Sarnobat
5

Ich weiß, dass ich ein paar Jahre zu spät bin, aber ich bin auf dieses Problem gestoßen und habe einen unorthodoxen Ansatz gewählt. Ich wollte es tun, ohne eine neue Klasse zu bilden, also habe ich mir Folgendes ausgedacht:

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
quelle
4

Parameterübergabe über die Methoden start () und 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
quelle
3

Sie können eine Klasse von Runnable ableiten und während der Konstruktion (z. B.) den Parameter übergeben.

Starten Sie es dann mit Thread.start (Runnable r).

Wenn Sie meinen, während der Thread ausgeführt wird, halten Sie einfach einen Verweis auf Ihr abgeleitetes Objekt im aufrufenden Thread und rufen Sie die entsprechenden Setter-Methoden auf (ggf. synchronisieren).

Brian Agnew
quelle
2

Es gibt eine einfache Möglichkeit, Parameter an ausführbare Dateien zu übergeben. Code:

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

Nein, Sie können keine Parameter an die run()Methode übergeben. Die Signatur sagt Ihnen das (es hat keine Parameter). Der wahrscheinlich einfachste Weg, dies zu tun, wäre die Verwendung eines speziell erstellten Objekts, das einen Parameter im Konstruktor übernimmt und in einer endgültigen Variablen speichert:

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

Sobald Sie dies getan haben, müssen Sie auf die Datenintegrität des Objekts achten, das Sie an die 'WorkingTask' übergeben. Die Daten sind jetzt in zwei verschiedenen Threads vorhanden, sodass Sie sicherstellen müssen, dass sie threadsicher sind.

Mihir Patel
quelle
1

Eine weitere Option; Mit diesem Ansatz können Sie das Element Runnable wie einen asynchronen Funktionsaufruf verwenden. Wenn Ihre Aufgabe kein Ergebnis zurückgeben muss, z. B. nur eine Aktion ausführt, müssen Sie sich keine Gedanken darüber machen, wie Sie ein "Ergebnis" zurückgeben.

Mit diesem Muster können Sie ein Element wiederverwenden, für das Sie einen internen Status benötigen. Wenn Parameter im Konstruktor nicht übergeben werden, ist Sorgfalt erforderlich, um den Programmzugriff auf Parameter zu vermitteln. Möglicherweise benötigen Sie weitere Überprüfungen, wenn Ihr Anwendungsfall verschiedene Anrufer usw. umfasst.

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 = neuer Thread (neuer MyRunnable (Parameter)); t.start ();

Wenn Sie ein Ergebnis der Verarbeitung benötigen, müssen Sie auch den Abschluss von MyRunnable koordinieren, wenn die Unteraufgabe abgeschlossen ist. Sie können einen Rückruf weiterleiten oder einfach auf den Thread 't' usw. warten.

werden
quelle
1

Speziell für Android

Für Rückrufzwecke implementiere ich normalerweise mein eigenes Generikum Runnablemit Eingabeparametern:

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

Die Verwendung ist einfach:

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

Im Manager:

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();
}
Andrei
quelle
1

Erstellen Sie eine lokale Variable in Ihrer Klasse, die extends Threadoder 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;
    }}

Auf diese Weise können Sie eine Variable übergeben, wenn Sie sie ausführen.

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

Die Ausgabe:

"www.google.com"
Calvin k
quelle