Come posso passare un parametro a un thread Java?


290

Qualcuno può suggerirmi come posso passare un parametro a un thread?

Inoltre, come funziona per le classi anonime?


5
Ti dispiacerebbe aggiungere qualche spiegazione in più esattamente quello che stai cercando di fare? Ci sono molte tecniche diverse, ma tutte dipendono dall'obiettivo finale.
Artem Barger,

4
Intendi passare un parametro a un thread già in esecuzione? Perché tutte le risposte attuali riguardano il passaggio dei parametri ai nuovi thread ...
Valentin Rocher,

Ora puoi usare Consumer<T>.
Alex78191,

Risposte:


371

È necessario passare il parametro nel costruttore all'oggetto Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

e invocarlo così:

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

7
@jasonm no, è un costruttore - i costruttori non hanno un tipo di ritorno.
Alnitak,

Questo significa che ogni thread costruito usando ravrà lo stesso argomento, quindi se vogliamo passare argomenti diversi a più thread in esecuzione MyThreadavremmo bisogno di creare una nuova MyThreadistanza usando l'argomento desiderato per ogni thread. In altre parole, per ottenere un thread attivo e funzionante dobbiamo creare due oggetti: Thread e MyThread. Questo è considerato cattivo, dal punto di vista delle prestazioni?
Isaac Kleinman,

2
@IsaacKleinman bene, devi farlo comunque, a meno che non estenda Thread. E questa soluzione funziona ancora in quel caso - basta cambiare "implementa Runnable" in "extends Thread", "Runnable" in "Thread" e "new Thread (r)" in "r".
user253751

111

Per le classi anonime:

In risposta alle modifiche alle domande, ecco come funziona per le classi anonime

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

Classi nominate:

Hai una classe che estende Thread (o implementa Runnable) e un costruttore con i parametri che desideri passare. Quindi, quando crei il nuovo thread, devi passare gli argomenti e quindi avviare il thread, in questo modo:

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

Runnable è una soluzione molto migliore di Thread BTW. Quindi preferirei:

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

Questa risposta è sostanzialmente la stessa di questa domanda simile: come passare i parametri a un oggetto Thread


2
Ho un codice simile al tuo esempio di classe anonima, tranne che accedo parameterdirettamente dal run()metodo senza usare un campo come pquello. Sembra funzionare C'è qualcosa di sottile multithreading che mi manca non copiando parameterin panticipo?
Randall Cook,

Penso che ti manchi un )primo esempio
Hack-R

Ho avuto la stessa esperienza di @RandallCook per la classe Anonimo. Finché avessi avuto la linea final X parameterprima new Runnable(), avrei potuto accedere parameterall'interno run(). Non avevo bisogno di fare il extra p = parameter.
Wisbucky

finalnon è più così importante; è sufficiente che la variabile sia effettivamente definitiva (nonostante non ne faccia nulla di male)
user85421

43

tramite il costruttore di una classe Runnable o 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 per mostrare come estendere Thread anziché implementare Runnable.
Caelum,

1
perché abbiamo bisogno del @Overrideper?
Neve,

@Snow the @Overrideafferma esplicitamente che sta sovrascrivendo il metodo astratto nella classe Thread.
wyskoj,

22

Questa risposta arriva molto tardi, ma forse qualcuno la troverà utile. Si tratta di come passare un parametro (i) a una Runnablesenza nemmeno dichiarare classe nominata (utile per gli 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();

Ovviamente puoi rimandare l'esecuzione startad un momento più conveniente o appropriato. E dipende da te quale sarà la firma del initmetodo (quindi potrebbero essere necessari più e / o diversi argomenti) e ovviamente anche il suo nome, ma in sostanza hai un'idea.

In effetti esiste anche un altro modo di passare un parametro a una classe anonima, con l'uso dei blocchi di inizializzazione. Considera questo:

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

Quindi tutto accade all'interno del blocco di inizializzazione.


In realtà mi è piaciuto molto questo ultimo esempio. Mi ricorda di usare una chiusura in JS per fare riferimento a variabili nell'ambito esterno. Ma this.myParamallora è davvero necessario? Non potresti semplicemente eliminare le variabili private e fare riferimento alla variabile dall'ambito esterno? Capisco (ovviamente) che questo ha alcune implicazioni, come la variabile che è aperta a cambiare dopo aver iniziato il thread.
Oligofren,

@JeffG ha effettivamente risposto a questa risposta nella risposta qui sotto!
Oligofren,

17

Quando si crea un thread, è necessaria un'istanza di Runnable. Il modo più semplice per passare un parametro sarebbe passarlo come argomento al costruttore:

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

Se poi vuoi cambiare il parametro mentre il thread è in esecuzione, puoi semplicemente aggiungere un metodo setter alla tua classe eseguibile:

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

Una volta ottenuto questo, è possibile modificare il valore del parametro chiamando in questo modo:

myRunnable.setMyParam("Goodbye World");

Naturalmente, se vuoi attivare un'azione quando il parametro viene modificato, dovrai usare i blocchi, il che rende le cose notevolmente più complesse.


1
L'aggiunta di un setter non crea potenziali condizioni di gara? Se il thread inizia con la variabile come un valore ma il setter lo modifica a metà esecuzione non sarebbe problematico?
anon58192932,

È vero, il setter e tutti gli accessi al parametro devono essere sincronizzati.
jwoolard,

9

Per creare un thread normalmente si crea la propria implementazione di Runnable. Passa i parametri al thread nel costruttore di questa classe.

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

Puoi estendere o e fornire i parametri come desideri. Ci sono semplici esempi nei documenti . Li porto qui: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

O scrivi una classe che implementa Runnable e passa tutto ciò di cui hai bisogno in un costruttore definito in modo appropriato, oppure scrivi una classe che estende Thread con un costruttore definito in modo appropriato che chiama super () con parametri appropriati.


6

A partire da Java 8, è possibile utilizzare un lambda per acquisire parametri effettivamente definitivi . Per esempio:

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

In Java 8 è possibile utilizzare lambdaespressioni con l' API di concorrenza e ExecutorServicecome sostituto di livello superiore per lavorare direttamente con i thread:

newCachedThreadPool()Crea un pool di thread che crea nuovi thread secondo necessità, ma riutilizza i thread precedentemente creati quando sono disponibili. Questi pool in genere miglioreranno le prestazioni dei programmi che eseguono molte attività asincrone di breve durata.

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

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

Vedi anche executors javadocs .


Finalmente un modo per farlo senza quei campi fastidiosi. Ora devo solo aspettare che il mio prodotto si aggiorni a Java 8.
Sridhar Sarnobat,

5

So di essere in ritardo di qualche anno, ma mi sono imbattuto in questo problema e ho adottato un approccio non ortodosso. Volevo farlo senza fare una nuova lezione, quindi questo è quello che mi è venuto in mente:

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

Parametro che passa tramite i metodi start () e 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

È possibile derivare una classe da Runnable e durante la costruzione (diciamo) passare il parametro in.

Quindi avviarlo utilizzando Thread.start (Runnable r);

Se intendi mentre il thread è in esecuzione, tieni semplicemente un riferimento all'oggetto derivato nel thread chiamante e chiama i metodi setter appropriati (sincronizzando dove appropriato)


2

Esiste un modo semplice per passare i parametri nei runnable. Codice:

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

2

No, non puoi trasmettere parametri al run()metodo. La firma ti dice che (non ha parametri). Probabilmente il modo più semplice per farlo sarebbe usare un oggetto appositamente costruito che prende un parametro nel costruttore e lo memorizza in una variabile finale:

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

Una volta fatto ciò, devi fare attenzione all'integrità dei dati dell'oggetto che passi nel "WorkingTask". I dati ora esistono in due thread diversi, quindi devi assicurarti che sia sicuro per i thread.


1

Un'ulteriore opzione; questo approccio consente di utilizzare l'elemento Runnable come una chiamata di funzione asincrona. Se la tua attività non ha bisogno di restituire un risultato, ad esempio esegue solo alcune azioni che non devi preoccuparti di come restituisci un "risultato".

Questo modello ti consente di riutilizzare un elemento, dove è necessario un qualche tipo di stato interno. Quando non si passano i parametri nella cura del costruttore è necessario mediare l'accesso dei programmi ai programmi. Potresti aver bisogno di più controlli se il tuo caso d'uso coinvolge diversi chiamanti, ecc.

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 (nuovo MyRunnable (parametro)); t.start ();

Se è necessario un risultato dell'elaborazione, sarà inoltre necessario coordinare il completamento di MyRunnable al termine dell'attività secondaria. Potresti rispondere a una chiamata o semplicemente attendere sul thread 't', ecc.


1

Specialmente per Android

Ai fini del callback di solito implemento il mio generico Runnablecon parametri di input:

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

L'uso è semplice:

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

Nel gestore:

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

Crea una variabile locale nella tua classe che extends Threado 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;
    }}

In questo modo, puoi passare una variabile quando la esegui.

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

Il risultato:

"www.google.com"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.