Como fazer um encadeamento Java esperar pela saída de outro encadeamento?

Estou fazendo um aplicativo Java com um thread de aplicativo-lógica e um thread de access ao database. Ambos persistem por toda a vida útil do aplicativo e ambos precisam estar em execução ao mesmo tempo (um fala com o servidor, um fala com o usuário; quando o aplicativo é iniciado, preciso que ambos funcionem).

No entanto, na boot, preciso ter certeza de que, inicialmente, o thread do aplicativo aguarde até que o thread do db esteja pronto (atualmente determinado pelo polling de um método personalizado dbthread.isReady() ). Eu não me importaria se blocos de thread de aplicativo até o segmento db estava pronto.

Thread.join() não parece uma solução – o encadeamento db só sai no encerramento do aplicativo.

while (!dbthread.isReady()) {} funciona, mas o loop vazio consome muitos ciclos de processador.

Alguma outra ideia? Obrigado.

Eu realmente recomendo que você faça um tutorial como o Java Concurrency da Sun antes de começar no mundo mágico do multithreading.

Há também vários livros bons (google for “Programação Simultânea em Java”, “Java Concurrency in Practice”).

Para chegar a sua resposta:

Em seu código que deve esperar pelo dbThread , você deve ter algo parecido com isto:

 //do some work synchronized(objectYouNeedToLockOn){ while (!dbThread.isReady()){ objectYouNeedToLockOn.wait(); } } //continue with work after dbThread is ready 

No método do dbThread , você precisaria fazer algo assim:

 //do db work synchronized(objectYouNeedToLockOn){ //set ready flag to true (so isReady returns true) ready = true; objectYouNeedToLockOn.notifyAll(); } //end thread run method here 

O objectYouNeedToLockOn que estou usando nesses exemplos é, de preferência, o object que você precisa manipular simultaneamente de cada thread, ou você poderia criar um Object separado para essa finalidade (eu não recomendaria que os próprios methods fossem sincronizados):

 private final Object lock = new Object(); //now use lock in your synchronized blocks 

Para aprofundar sua compreensão:
Existem outras maneiras (às vezes melhores) de fazer o que foi dito acima, por exemplo, com CountdownLatches , etc. Desde o Java 5, há muitas classs de simultaneidade interessantes no pacote e nos subpacotes java.util.concurrent . Você realmente precisa encontrar material on-line para conhecer a simultaneidade ou obter um bom livro.

Use um CountDownLatch com um contador de 1.

 CountDownLatch latch = new CountDownLatch(1); 

Agora no tópico do aplicativo

 latch.await(); 

No encadeamento do database, depois de terminar, faça –

 latch.countDown(); 

Requerimento ::

  1. Para aguardar a execução do próximo thread até o final anterior.
  2. O próximo segmento não deve iniciar até que o segmento anterior pare, independentemente do consumo de tempo.
  3. Deve ser simples e fácil de usar.

Responda ::

@Ver java.util.concurrent.Future.get () doc.

future.get () Espera, se necessário, que o cálculo seja concluído e, em seguida, recupera seu resultado.

Tarefa concluída!! Veja o exemplo abaixo

 import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; public class ThreadTest { public void print(String m) { System.out.println(m); } public class One implements Callable { public Integer call() throws Exception { print("One..."); Thread.sleep(6000); print("One!!"); return 100; } } public class Two implements Callable { public String call() throws Exception { print("Two..."); Thread.sleep(1000); print("Two!!"); return "Done"; } } public class Three implements Callable { public Boolean call() throws Exception { print("Three..."); Thread.sleep(2000); print("Three!!"); return true; } } /** * @See java.util.concurrent.Future.get() doc * 

* Waits if necessary for the computation to complete, and then * retrieves its result. */ @Test public void poolRun() throws InterruptedException, ExecutionException { int n = 3; // Build a fixed number of thread pool ExecutorService pool = Executors.newFixedThreadPool(n); // Wait until One finishes it's task. pool.submit(new One()).get(); // Wait until Two finishes it's task. pool.submit(new Two()).get(); // Wait until Three finishes it's task. pool.submit(new Three()).get(); pool.shutdown(); } }

Saída deste programa ::

 One... One!! Two... Two!! Three... Three!! 

Você pode ver que leva 6sec antes de terminar sua tarefa, que é maior que outro thread. Então Future.get () aguarda até que a tarefa esteja concluída.

Se você não usa future.get (), ele não espera terminar e executa o consumo de tempo com base.

Boa sorte com a simultaneidade de Java.

 public class ThreadEvent { private final Object lock = new Object(); public void signal() { synchronized (lock) { lock.notify(); } } public void await() throws InterruptedException { synchronized (lock) { lock.wait(); } } } 

Use esta class como esta então:

Crie um ThreadEvent:

 ThreadEvent resultsReady = new ThreadEvent(); 

No método, isso está esperando pelos resultados:

 resultsReady.await(); 

E no método que está criando os resultados depois que todos os resultados foram criados:

 resultsReady.signal(); 

EDITAR:

(Desculpe por editar este post, mas este código tem uma condição de corrida muito ruim e eu não tenho reputação suficiente para comentar)

Você só pode usar isso se tiver 100% de certeza de que signal () é chamado após await (). Esse é o grande motivo pelo qual você não pode usar o object Java como, por exemplo, events do Windows.

O se o código é executado nesta ordem:

 Thread 1: resultsReady.signal(); Thread 2: resultsReady.await(); 

então o segmento 2 irá esperar para sempre . Isso ocorre porque Object.notify () somente acorda um dos segmentos em execução no momento. Um thread esperando depois não é acordado. Isso é muito diferente de como espero que os events funcionem, em que um evento é sinalizado até a) aguardado ou b) explicitamente redefinido.

Nota: Na maioria das vezes, você deve usar notifyAll (), mas isso não é relevante para o problema “wait forever” acima.

Tente a class CountDownLatch fora do pacote java.util.concurrent , que fornece mecanismos de synchronization de nível mais alto, que são muito menos propensos a erros do que qualquer material de baixo nível.

Você poderia fazer isso usando um object Exchanger compartilhado entre os dois segmentos:

 private Exchanger myDataExchanger = new Exchanger(); // Wait for thread's output String data; try { data = myDataExchanger.exchange(""); } catch (InterruptedException e1) { // Handle Exceptions } 

E no segundo segmento:

 try { myDataExchanger.exchange(data) } catch (InterruptedException e) { } 

Como outros já disseram, não aceite este código leve e copiado. Faça algumas leituras primeiro.

A interface Future do pacote java.lang.concurrent foi projetada para fornecer access aos resultados calculados em outro thread.

Dê uma olhada no FutureTask e ExecutorService para uma maneira pronta de fazer esse tipo de coisa.

Eu recomendo fortemente ler Java Concurrency In Practice para qualquer pessoa interessada em simultaneidade e multithreading. Ele obviamente se concentra em Java, mas há muita carne para quem trabalha em outros idiomas também.

Muitas respostas corretas, mas sem um exemplo simples. Aqui está uma maneira fácil e simples de usar o CountDownLatch :

 //inside your currentThread.. lets call it Thread_Main //1 final CountDownLatch latch = new CountDownLatch(1); //2 // launch thread#2 new Thread(new Runnable() { @Override public void run() { //4 //do your logic here in thread#2 //then release the lock //5 latch.countDown(); } }).start(); try { //3 this method will block the thread of latch untill its released later from thread#2 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } //6 // You reach here after latch.countDown() is called from thread#2 

Se você quiser algo rápido e sujo, você pode simplesmente adicionar uma chamada Thread.sleep () dentro do seu loop while. Se a biblioteca de database é algo que você não pode mudar, então não há outra solução fácil. Verificar o database até que esteja pronto com um período de espera não elimina o desempenho.

 while (!dbthread.isReady()) { Thread.sleep(250); } 

Dificilmente algo que você poderia chamar de código elegante, mas faz o trabalho.

Caso você possa modificar o código do database, usar um mutex como proposto em outras respostas é melhor.

Isso se aplica a todos os idiomas:

Você quer ter um modelo de evento / ouvinte. Você cria um ouvinte para aguardar um evento específico. O evento seria criado (ou sinalizado) no seu segmento de trabalho. Isso bloqueará o encadeamento até que o sinal seja recebido, em vez de pesquisar constantemente para ver se uma condição é atendida, como a solução que você tem atualmente.

Sua situação é uma das causas mais comuns de deadlocks – certifique-se de sinalizar o outro thread, independentemente dos erros que possam ter ocorrido. Exemplo – se seu aplicativo lança uma exceção – e nunca chama o método para sinalizar ao outro que as coisas foram concluídas. Isso fará com que o outro segmento nunca “acorde”.

Sugiro que você analise os conceitos de uso de events e manipuladores de events para entender melhor esse paradigma antes de implementar seu caso.

Como alternativa, você pode usar uma chamada de function de bloqueio usando um mutex – que fará com que o thread aguarde o recurso ficar livre. Para fazer isso, você precisa de uma boa synchronization de threads, como:

 Thread-A Locks lock-a Run thread-B Thread-B waits for lock-a Thread-A unlocks lock-a (causing Thread-B to continue) Thread-A waits for lock-b Thread-B completes and unlocks lock-b 

Você pode ler a partir de uma fila de bloqueio em um thread e gravar em outro thread.

Desde a

  1. join() foi descartado
  2. você já usa o CountDownLatch e
  3. Future.get () já é proposto por outros especialistas,

Você pode considerar outras alternativas:

  1. invokeAll de ExecutorService

     invokeAll(Collection< ? extends Callable> tasks) 

    Executa as tarefas dadas, retornando uma lista de futuros mantendo seu status e resultados quando tudo estiver concluído.

  2. ForkJoinPool ou newWorkStealingPool de Executors (desde o lançamento do Java 8)

    Cria um conjunto de encadeamentos de roubo de trabalho usando todos os processadores disponíveis como seu nível de paralelismo de destino.

insira a descrição da imagem aqui

Essa ideia pode ser aplicada. Se você usar CountdownLatches ou Semaphores funciona perfeito, mas se você está procurando a resposta mais fácil para uma entrevista eu acho que isso pode ser aplicado.