¿Cómo puedo pasar un parámetro a un hilo de Java?


290

¿Alguien puede sugerirme cómo puedo pasar un parámetro a un hilo?

Además, ¿cómo funciona para las clases anónimas?


55
¿Te importaría agregar alguna explicación adicional de eso exactamente lo que estás tratando de hacer? Existen muchas técnicas, pero todas dependen del objetivo final.
Artem Barger

44
¿Te refieres a pasar un parámetro a un hilo ya en ejecución? Debido a que todas las respuestas actuales están a punto de pasar parámetros a nuevos temas ...
Valentin Rocher

Ahora puedes usar Consumer<T>.
Alex78191

Respuestas:


371

Debe pasar el parámetro en el constructor al objeto Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

e invocarlo así:

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

77
@jasonm no, es un constructor, los constructores no tienen un tipo de retorno.
Alnitak

Esto significa que cada subproceso construido usando rtendrá el mismo argumento, por lo que si queremos pasar diferentes argumentos a múltiples subprocesos en ejecución, MyThreadtendremos que crear una nueva MyThreadinstancia usando el argumento deseado para cada subproceso. En otras palabras, para poner en marcha un hilo necesitamos crear dos objetos: Thread y MyThread. ¿Se considera esto malo en cuanto al rendimiento?
Isaac Kleinman

2
@IsaacKleinman bueno, tienes que hacer eso de todos modos, a menos que extiendas Thread. Y esta solución aún funciona en ese caso: simplemente cambie "implementa Runnable" a "extiende Thread", "Runnable" a "Thread" y "new Thread (r)" a "r".
user253751

111

Para clases anónimas:

En respuesta a las ediciones de preguntas aquí es cómo funciona para las clases anónimas

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

Clases con nombre:

Tiene una clase que extiende Thread (o implementa Runnable) y un constructor con los parámetros que desea pasar. Luego, cuando crea el nuevo hilo, debe pasar los argumentos y luego comenzar el hilo, algo como esto:

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

Runnable es una solución mucho mejor que Thread BTW. Entonces preferiría:

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

Esta respuesta es básicamente la misma que esta pregunta similar: cómo pasar parámetros a un objeto Thread


2
Tengo un código similar a su ejemplo de clase anónima, excepto que accedo parameterdirectamente desde el run()método sin usar un campo como pen absoluto. Parece funcionar. ¿Hay alguna cosa sutil multihilo que me falta al no copiar parameterde pantemano?
Randall Cook

Creo que te estás perdiendo un )en el primer ejemplo
Hack-R

Tuve la misma experiencia que @RandallCook para la clase Anónimo. Mientras tuviera final X parameterantes de la new Runnable()línea, podría acceder al parameterinterior run(). No necesitaba necesitar hacer el extra p = parameter.
wisbucky

finalya no es tan importante; es suficiente si la variable es efectivamente definitiva (a pesar de que no hace daño tenerlo)
user85421

43

a través del constructor de una clase 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();
}

55
+1 para mostrar cómo hacerlo extendiendo Thread en lugar de implementar Runnable.
Caelum

1
¿Por qué necesitamos el @Overridepara?
Nieve

@Snow @Overridedice explícitamente que está anulando el método abstracto en la clase Thread.
wyskoj

22

Esta respuesta llega muy tarde, pero tal vez alguien la encuentre útil. Se trata de cómo pasar un parámetro (s) a un Runnablesin siquiera declarar la clase nombrada (útil para los 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();

Por supuesto, puede posponer la ejecución de startun momento más conveniente o apropiado. Y depende de usted cuál será la firma del initmétodo (por lo que puede tomar más y / o argumentos diferentes) y, por supuesto, incluso su nombre, pero básicamente se hace una idea.

De hecho, también hay otra forma de pasar un parámetro a una clase anónima, con el uso de los bloques inicializadores. Considera esto:

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

Entonces todo sucede dentro del bloque inicializador.


De hecho, este último ejemplo me gustó bastante bien. Me recuerda utilizar un cierre en JS para hacer referencia a variables en el ámbito externo. ¿Pero es this.myParamrealmente necesario entonces? ¿No podría simplemente descartar las variables privadas y referirse a la variable desde el ámbito externo? Entiendo (por supuesto) que esto tiene algunas implicaciones, como que la variable esté abierta para cambiar después de comenzar el hilo.
oligofren

¡@JeffG realmente respondió esto en la respuesta a continuación!
oligofren

17

Cuando crea un hilo, necesita una instancia de Runnable. La forma más fácil de pasar un parámetro sería pasarlo como argumento al constructor:

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 luego desea cambiar el parámetro mientras se está ejecutando el subproceso, simplemente puede agregar un método de establecimiento a su clase ejecutable:

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

Una vez que tenga esto, puede cambiar el valor del parámetro llamando así:

myRunnable.setMyParam("Goodbye World");

Por supuesto, si desea activar una acción cuando se cambia el parámetro, tendrá que usar bloqueos, lo que hace que las cosas sean considerablemente más complejas.


1
¿Agregar un setter no crea condiciones potenciales de carrera? Si el subproceso comienza con la variable como un valor pero el configurador lo cambia a mitad de la ejecución, ¿no sería problemático?
anon58192932

Es cierto que el setter y todos los accesos al parámetro deben estar sincronizados.
jwoolard

9

Para crear un hilo, normalmente crea su propia implementación de Runnable. Pase los parámetros al hilo en el constructor de esta clase.

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

Puede extender el o el y proporcionar los parámetros que desee. Hay ejemplos simples en los documentos . Los portaré aquí: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

Escriba una clase que implemente Runnable y pase lo que necesite en un constructor adecuadamente definido, o escriba una clase que extienda Thread con un constructor adecuadamente definido que llame a super () con los parámetros apropiados.


6

A partir de Java 8, puede usar una lambda para capturar parámetros que sean efectivamente finales . Por ejemplo:

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

En Java 8 puede usar lambdaexpresiones con la API de concurrencia y ExecutorServicecomo un reemplazo de nivel superior para trabajar con hilos directamente:

newCachedThreadPool()Crea un grupo de subprocesos que crea nuevos subprocesos según sea necesario, pero reutilizará los subprocesos construidos previamente cuando estén disponibles. Estos grupos generalmente mejorarán el rendimiento de los programas que ejecutan muchas tareas asincrónicas de corta duración.

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

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

Ver también executors javadocs .


Finalmente una forma de hacerlo sin esos molestos campos. Ahora solo tengo que esperar a que mi producto se actualice a Java 8.
Sridhar Sarnobat

5

Sé que llego unos años tarde, pero me encontré con este problema y adopté un enfoque poco ortodoxo. Quería hacerlo sin hacer una nueva clase, así que esto es lo que se me ocurrió:

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

Parámetro que pasa por los métodos start () y 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

Puede derivar una clase de Runnable, y durante la construcción (digamos) pasar el parámetro.

Luego ejecútalo usando Thread.start (Runnable r);

Si quiere decir mientras el subproceso se está ejecutando, simplemente mantenga una referencia a su objeto derivado en el subproceso de llamada y llame a los métodos de establecimiento apropiados (sincronización cuando corresponda)


2

Hay una manera simple de pasar parámetros a ejecutables. Código:

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

2

No, no puede pasar parámetros al run()método. La firma te dice que (no tiene parámetros). Probablemente la forma más fácil de hacer esto sería usar un objeto especialmente diseñado que tome un parámetro en el constructor y lo almacene en una variable final:

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 vez que haga eso, debe tener cuidado con la integridad de los datos del objeto que pasa a la 'WorkingTask'. Los datos ahora existirán en dos subprocesos diferentes, por lo que debe asegurarse de que sean seguros para subprocesos.


1

Una opción más; Este enfoque le permite utilizar el elemento Runnable como una llamada de función asincrónica. Si su tarea no necesita devolver un resultado, por ejemplo, solo realiza alguna acción, no necesita preocuparse por cómo reenvía un "resultado".

Este patrón le permite reutilizar un elemento, donde necesita algún tipo de estado interno. Cuando no se pasan parámetros en el constructor, se necesita cuidado para mediar el acceso de los programas a los parámetros. Es posible que necesite más verificaciones si su caso de uso involucra a diferentes personas que llaman, 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 );
      }

  }

}

Hilo t = nuevo Hilo (nuevo MyRunnable (parámetro)); t.start ();

Si necesita un resultado del procesamiento, también deberá coordinar la finalización de MyRunnable cuando finalice la subtarea. Puede devolver una llamada o simplemente esperar en el hilo 't', etc.


1

Especial para Android

Para fines de devolución de llamada, generalmente implemento mi propio genérico Runnablecon parámetros de entrada:

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

El uso es simple:

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

En gerente:

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

Cree una variable local en su clase que 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;
    }}

De esta manera, puede pasar una variable cuando la ejecuta.

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

La salida:

"www.google.com"
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.