Como posso passar um parâmetro para um Java Thread?

290

Alguém pode me sugerir como eu posso passar um parâmetro para um thread?

Além disso, como funciona para classes anônimas?

Steve
fonte
5
Você gostaria de adicionar uma explicação extra sobre o que exatamente você está tentando fazer? Existem várias técnicas para isso, mas todas elas dependem do objetivo final.
Artem Barger
4
Você quer dizer passar um parâmetro para um thread já em execução? Porque todas as respostas atuais são sobre passando parâmetros para novos tópicos ...
Valentin Rocher
Agora você pode usar Consumer<T>.
Alex78191

Respostas:

371

Você precisa passar o parâmetro no construtor para o objeto Runnable:

public class MyRunnable implements Runnable {

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

   public void run() {
   }
}

e invoque-o assim:

Runnable r = new MyRunnable(param_value);
new Thread(r).start();
Alnitak
fonte
7
@jasonm não, é um construtor - os construtores não têm um tipo de retorno.
Alnitak
Isso significa que cada Thread construído usando rterá o mesmo argumento; portanto, se quisermos passar argumentos diferentes para vários Threads em execução MyThread, precisaremos criar uma nova MyThreadinstância usando o argumento desejado para cada Thread. Em outras palavras, para colocar um thread em funcionamento, precisamos criar dois objetos: Thread e MyThread. Isso é considerado ruim, em termos de desempenho?
Isaac Kleinman 15/10
2
@IsaacKleinman bem, você precisa fazer isso de qualquer maneira, a menos que estenda o Thread. E essa solução ainda funciona nesse caso - basta alterar "implementa Runnable" para "estende o Thread", "Runnable" para "Thread" e "new Thread (r)" para "r".
User253751
111

Para classes anônimas:

Em resposta às edições das perguntas, veja como funciona para classes anônimas

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

Classes nomeadas:

Você tem uma classe que estende o Thread (ou implementa Runnable) e um construtor com os parâmetros que deseja passar. Então, quando você cria o novo thread, precisa passar os argumentos e iniciar o thread, algo como isto:

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

Runnable é uma solução muito melhor que o Thread BTW. Então, eu prefiro:

   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 resposta é basicamente a mesma que esta pergunta semelhante: Como passar parâmetros para um objeto Thread

Nick Fortescue
fonte
2
Eu tenho um código semelhante ao seu exemplo de classe anônima, exceto pelo acesso parameterdireto a partir do run()método sem usar um campo como o pmesmo. Parece funcionar. Existe alguma multithreading coisa sutil que estou em falta por não copiar parametera pde antemão?
Randall Cozinhe
Eu acho que você está faltando um )no primeiro exemplo
Hack-R
Eu tive a mesma experiência que a classe @RandallCook for Anonymous. Contanto que eu tivesse final X parameterantes da new Runnable()linha, então eu poderia acessar parameterdentro run(). Eu não precisava fazer o extra p = parameter.
Wisbucky 18/05/19
finalnão é mais tão importante assim; é o suficiente se a variável é efetivamente finais (apesar não faz mal tê-lo)
user85421
43

via construtor de uma 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();
}
dfa
fonte
5
+1 por mostrar como fazer isso estendendo o Thread em vez de implementar o Runnable.
Caelum
1
por que precisamos do @Overridepara?
Snow
@ Snow @Overridedeclara explicitamente que está substituindo o método abstrato na classe Thread.
Wyskoj 15/03/19
22

Essa resposta chega muito tarde, mas talvez alguém a ache útil. É sobre como passar um (s) parâmetro (s) para um Runnablesem nem mesmo declarar uma classe nomeada (útil para 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();

É claro que você pode adiar a execução startpara um momento mais conveniente ou apropriado. E depende de você qual será a assinatura do initmétodo (para que possa levar mais e / ou argumentos diferentes) e, claro, até o nome, mas basicamente você tem uma idéia.

De fato, há também outra maneira de passar um parâmetro para uma classe anônima, com o uso dos blocos inicializadores. Considere isto:

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

Então tudo acontece dentro do bloco inicializador.

Cromax
fonte
Na verdade, gostei muito desse último exemplo. Lembra-me de usar um fechamento em JS para referenciar variáveis ​​no escopo externo. Mas o this.myParamentão é realmente necessário? Você não pode simplesmente eliminar as variáveis ​​privadas e se referir à variável do escopo externo? Entendo (é claro) que isso tem algumas implicações, como a variável aberta a alterações após o início do thread.
Oligofren
@JeffG realmente respondeu isso na resposta abaixo!
Oligofren
17

Quando você cria um encadeamento, precisa de uma instância de Runnable. A maneira mais fácil de passar um parâmetro seria passá-lo como um argumento para o construtor:

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 você quiser alterar o parâmetro enquanto o encadeamento estiver em execução, basta adicionar um método setter à sua classe executável:

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

Depois de ter isso, você pode alterar o valor do parâmetro chamando assim:

myRunnable.setMyParam("Goodbye World");

Obviamente, se você quiser acionar uma ação quando o parâmetro for alterado, precisará usar bloqueios, o que torna as coisas consideravelmente mais complexas.

jwoolard
fonte
1
A adição de um levantador não cria condições de corrida em potencial? Se o encadeamento começa com a variável como um valor, mas o setter a altera no meio da execução, isso não seria problemático?
anon58192932
É verdade que o setter e todo acesso ao parâmetro devem ser sincronizados.
jwoolard
9

Para criar um thread, você normalmente cria sua própria implementação do Runnable. Passe os parâmetros para o thread no construtor desta 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);
   }
}
Mnementh
fonte
8

Você pode estender o ou oe fornecer os parâmetros que desejar. Existem exemplos simples nos documentos . Vou portá-los aqui: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
fonte
7

Escreva uma classe que implemente Runnable e passe o que for necessário em um construtor definido adequadamente ou escreva uma classe que estenda o Thread com um construtor definido adequadamente que chame super () com os parâmetros apropriados.

PaulJWilliams
fonte
6

No Java 8, você pode usar um lambda para capturar parâmetros efetivamente finais . Por exemplo:

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
fonte
5

No Java 8, você pode usar lambdaexpressões com a API de simultaneidade e ExecutorServicecomo uma substituição de nível superior para trabalhar diretamente com threads:

newCachedThreadPool()Cria um conjunto de encadeamentos que cria novos encadeamentos, conforme necessário, mas reutiliza encadeamentos construídos anteriormente quando estiverem disponíveis. Esses pools geralmente melhoram o desempenho dos programas que executam muitas tarefas assíncronas de curta duração.

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

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

Veja também executors javadocs .

Stuart Cardall
fonte
Finalmente, uma maneira de fazê-lo sem esses campos irritantes. Agora eu só preciso esperar para o meu produto para o upgrade para Java 8.
Sridhar Sarnobat
5

Sei que estou alguns anos atrasado, mas me deparei com essa questão e adotei uma abordagem pouco ortodoxa. Eu queria fazer isso sem fazer uma nova aula, então é isso que eu criei:

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

Passagem de parâmetro pelos métodos 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);
}
Java42
fonte
3

Você pode derivar uma classe de Runnable e, durante a construção (digamos), passar o parâmetro

Em seguida, inicie-o usando Thread.start (Runnable r);

Se você quer dizer enquanto o encadeamento está em execução, simplesmente mantenha uma referência ao seu objeto derivado no encadeamento de chamada e chame os métodos apropriados do setter (sincronizando quando apropriado)

Brian Agnew
fonte
2

Existe uma maneira simples de passar parâmetros para as executáveis. Código:

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

Não, você não pode passar parâmetros para o run()método. A assinatura informa que (ele não possui parâmetros). Provavelmente, a maneira mais fácil de fazer isso seria usar um objeto criado especificamente para obter um parâmetro no construtor e armazená-lo em uma variável 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();

Depois de fazer isso - você deve ter cuidado com a integridade dos dados do objeto que passa para a 'WorkingTask'. Os dados agora existirão em dois threads diferentes, portanto, você deve garantir que seja Thread Safe.

Mihir Patel
fonte
1

Mais uma opção; Essa abordagem permite usar o item Runnable como uma chamada de função assíncrona. Se a sua tarefa não precisar retornar um resultado, por exemplo, apenas executar alguma ação, você não precisará se preocupar com a forma como repassa um "resultado".

Esse padrão permite reutilizar um item, no qual você precisa de algum tipo de estado interno. Quando não for passando parâmetro (s) no cuidado do construtor, é necessário mediar o acesso dos programas aos parâmetros. Você pode precisar de mais verificações se o seu caso de uso envolver chamadas diferentes 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 = new Thread (novo MyRunnable (parâmetro)); t.start ();

Se você precisar de um resultado do processamento, também precisará coordenar a conclusão do MyRunnable quando a subtarefa terminar. Você pode passar uma chamada de volta ou apenas esperar no Thread 't', etc.

vai
fonte
1

Especialmente para Android

Para fins de retorno de chamada, geralmente implemento meu próprio genérico Runnablecom parâmetro (s) de entrada:

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

O uso é simples:

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

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

Crie uma variável local em sua classe que 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;
    }}

Dessa forma, você pode passar uma variável ao executá-la.

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

A saída:

"www.google.com"
calvin k
fonte