Comment passer un paramètre à un thread Java?


290

Quelqu'un peut-il me suggérer comment passer un paramètre à un fil?

En outre, comment cela fonctionne-t-il pour les classes anonymes?


5
Pourriez-vous ajouter des explications supplémentaires sur ce que vous essayez exactement de faire? Il existe de nombreuses techniques, mais toutes dépendent du but final.
Artem Barger

4
Voulez-vous dire passer un paramètre à un thread déjà en cours d'exécution? Parce que toutes les réponses actuelles concernent le passage de paramètres à de nouveaux threads ...
Valentin Rocher

Vous pouvez maintenant utiliser Consumer<T>.
Alex78191

Réponses:


371

Vous devez passer le paramètre dans le constructeur à l'objet Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

et l'invoquer ainsi:

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

7
@jasonm non, c'est un constructeur - les constructeurs n'ont pas de type de retour.
Alnitak du

Cela signifie que chaque thread construit en utilisant raura le même argument, donc si nous voulons passer différents arguments à plusieurs threads en cours d'exécution, MyThreadnous devons créer une nouvelle MyThreadinstance en utilisant l'argument souhaité pour chaque thread. En d'autres termes, pour obtenir un thread opérationnel, nous devons créer deux objets: Thread et MyThread. Est-ce considéré comme mauvais sur le plan des performances?
Isaac Kleinman

2
@IsaacKleinman, vous devez le faire de toute façon, sauf si vous prolongez Thread. Et cette solution fonctionne toujours dans ce cas - il suffit de changer "implémente Runnable" en "étend Thread", "Runnable" en "Thread" et "new Thread (r)" en "r".
user253751

111

Pour les cours anonymes:

En réponse aux modifications apportées aux questions, voici comment cela fonctionne pour les classes anonymes

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

Classes nommées:

Vous avez une classe qui étend Thread (ou implémente Runnable) et un constructeur avec les paramètres que vous souhaitez transmettre. Ensuite, lorsque vous créez le nouveau thread, vous devez passer les arguments, puis démarrer le thread, quelque chose comme ceci:

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

Runnable est une bien meilleure solution que Thread BTW. Je préfère donc:

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

Cette réponse est fondamentalement la même que cette question similaire: Comment passer des paramètres à un objet Thread


2
J'ai un code similaire à votre exemple de classe anonyme, sauf que j'accède parameterdirectement à partir de la run()méthode sans utiliser un champ comme pdu tout. Cela semble fonctionner. Y a-t-il quelque chose de multithread subtil qui me manque en ne copiant pas parameterà l' pavance?
Randall Cook

Je pense qu'il vous manque un )dans le premier exemple
Hack-R

J'ai eu la même expérience que @RandallCook pour la classe Anonymous. Tant que j'avais final X parameterla new Runnable()ligne avant , je pouvais accéder à l' parameterintérieur run(). Je n'avais pas besoin de faire le supplément p = parameter.
wisbucky

finaln'est plus si important; c'est suffisant si la variable est effectivement finale (malgré cela ne fait pas de mal de l'avoir)
user85421

43

via le constructeur d'une classe Runnable ou 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 pour avoir montré comment le faire en étendant Thread au lieu d'implémenter Runnable.
Caelum

1
pourquoi avons-nous besoin du @Overridepour?
Neige

@Snow le @Overridedéclare explicitement qu'il remplace la méthode abstraite dans la classe Thread.
wyskoj

22

Cette réponse arrive très tard, mais peut-être que quelqu'un la trouvera utile. Il s'agit de savoir comment passer un ou plusieurs paramètres à une Runnablesans même déclarer de classe nommée (pratique pour les inliners):

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

Bien sûr, vous pouvez reporter l'exécution startà un moment plus pratique ou approprié. Et c'est à vous de décider quelle sera la signature de la initméthode (donc cela peut prendre plus et / ou différents arguments) et bien sûr même son nom, mais en gros vous avez une idée.

En fait, il existe également une autre façon de passer un paramètre à une classe anonyme, avec l'utilisation des blocs d'initialisation. Considère ceci:

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

Tout se passe donc à l'intérieur du bloc d'initialisation.


En fait, j'ai bien aimé ce dernier exemple. Me rappelle d'utiliser une fermeture dans JS pour référencer des variables dans la portée externe. Mais est-ce this.myParamalors vraiment nécessaire? Ne pourriez-vous pas simplement supprimer les variables privées et vous référer à la variable de la portée externe? Je comprends (bien sûr) que cela a des implications, telles que la variable pouvant être modifiée après le démarrage du thread.
oligofren

@JeffG a effectivement répondu à cela dans la réponse ci-dessous!
oligofren

17

Lorsque vous créez un thread, vous avez besoin d'une instance de Runnable. Le moyen le plus simple de passer un paramètre serait de le passer en argument au constructeur:

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

Si vous souhaitez ensuite modifier le paramètre pendant l'exécution du thread, vous pouvez simplement ajouter une méthode de définition à votre classe exécutable:

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

Une fois que vous avez ceci, vous pouvez changer la valeur du paramètre en appelant comme ceci:

myRunnable.setMyParam("Goodbye World");

Bien sûr, si vous souhaitez déclencher une action lorsque le paramètre est modifié, vous devrez utiliser des verrous, ce qui rend les choses considérablement plus complexes.


1
L'ajout d'un setter ne crée-t-il pas des conditions de course potentielles? Si le thread commence avec la variable comme une seule valeur mais que le passeur la modifie en cours d'exécution, cela ne serait-il pas problématique?
anon58192932

Certes, le setter et tous les accès au paramètre doivent être synchronisés.
jwoolard

9

Pour créer un thread, vous créez normalement votre propre implémentation de Runnable. Passez les paramètres au thread dans le constructeur de cette 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

Vous pouvez étendre le ou le et fournir les paramètres que vous souhaitez. Il y a des exemples simples dans les documents . Je vais les porter ici: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

Soit écrivez une classe qui implémente Runnable et passez tout ce dont vous avez besoin dans un constructeur correctement défini, soit écrivez une classe qui étend Thread avec un constructeur correctement défini qui appelle super () avec les paramètres appropriés.


6

Depuis Java 8, vous pouvez utiliser un lambda pour capturer des paramètres qui sont effectivement définitifs . Par exemple:

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

Dans Java 8, vous pouvez utiliser des lambdaexpressions avec l' API de concurrence et le ExecutorServicecomme remplacement de niveau supérieur pour travailler directement avec les threads:

newCachedThreadPool()Crée un pool de threads qui crée de nouveaux threads selon les besoins, mais réutilisera les threads précédemment construits lorsqu'ils seront disponibles. Ces pools améliorent généralement les performances des programmes qui exécutent de nombreuses tâches asynchrones de courte durée.

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

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

Voir aussi executors javadocs .


Enfin un moyen de le faire sans ces champs ennuyeux. Il ne me
reste plus

5

Je sais que j'ai quelques années de retard, mais je suis tombé sur ce problème et j'ai adopté une approche peu orthodoxe. Je voulais le faire sans faire de nouveau cours, c'est donc ce que j'ai trouvé:

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

Paramètre passant par les méthodes start () et 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

Vous pouvez dériver une classe de Runnable, et pendant la construction (par exemple) passer le paramètre.

Ensuite, lancez-le en utilisant Thread.start (Runnable r);

Si vous voulez dire pendant que le thread est en cours d'exécution, maintenez simplement une référence à votre objet dérivé dans le thread appelant et appelez les méthodes de définition appropriées (synchronisation le cas échéant)


2

Il existe un moyen simple de passer des paramètres dans des exécutables. Code:

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

2

Non, vous ne pouvez pas transmettre de paramètres à la run()méthode. La signature vous l'indique (elle n'a pas de paramètres). La façon la plus simple de le faire serait probablement d'utiliser un objet spécialement conçu qui prend un paramètre dans le constructeur et le stocke dans une variable 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();

Une fois que vous avez fait cela, vous devez faire attention à l'intégrité des données de l'objet que vous passez dans la «WorkingTask». Les données existeront désormais dans deux threads différents, vous devez donc vous assurer qu'elles sont Thread Safe.


1

Une autre option; cette approche vous permet d'utiliser l'élément Runnable comme un appel de fonction asynchrone. Si votre tâche n'a pas besoin de renvoyer un résultat, par exemple, elle exécute simplement une action, vous n'avez pas à vous soucier de la façon dont vous transmettez un "résultat".

Ce modèle vous permet de réutiliser un élément, où vous avez besoin d'une sorte d'état interne. Lorsqu'il ne transmet pas de paramètre (s) dans le constructeur, une attention particulière est nécessaire pour assurer l'accès des programmes aux paramètres. Vous devrez peut-être plus de vérifications si votre cas d'utilisation implique différents appelants, etc.

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 = nouveau Thread (nouveau MyRunnable (paramètre)); t.start ();

Si vous avez besoin d'un résultat de traitement, vous devrez également coordonner l'achèvement de MyRunnable à la fin de la sous-tâche. Vous pouvez renvoyer un appel ou simplement attendre le Thread 't', etc.


1

Spécialement pour Android

À des fins de rappel, j'implémente généralement mon propre générique Runnableavec des paramètres d'entrée:

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

L'utilisation est simple:

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

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

1

Créez une variable locale dans votre classe extends Threadou 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;
    }}

De cette façon, vous pouvez passer une variable lorsque vous l'exécutez.

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

Le résultat:

"www.google.com"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.