Eu tenho um objeto com um método chamado StartDownload()
, que inicia três threads.
Como recebo uma notificação quando cada thread termina a execução?
Existe uma maneira de saber se um (ou todos) do thread está concluído ou ainda está em execução?
java
multithreading
Ricardo Felgueiras
fonte
fonte
Respostas:
Existem várias maneiras de fazer isso:
Como implementar a Idéia # 5? Bem, uma maneira é primeiro criar uma interface:
em seguida, crie a seguinte classe:
e cada um dos seus Threads será estendido
NotifyingThread
e, em vez de implementá-run()
lo, será implementadodoRun()
. Assim, quando concluírem, notificarão automaticamente qualquer pessoa que aguarde a notificação.Finalmente, em sua classe principal - aquela que inicia todos os Threads (ou pelo menos o objeto que está aguardando notificação) - modifique essa classe para
implement ThreadCompleteListener
e imediatamente após a criação de cada Thread, adicione-se à lista de ouvintes:então, à medida que cada thread sai, seu
notifyOfThreadComplete
método será chamado com a instância Thread que acabou de concluir (ou travar).Note-se que melhor seria
implements Runnable
, em vez deextends Thread
paraNotifyingThread
como a extensão da linha é geralmente desencorajado em novo código. Mas estou codificando para sua pergunta. Se você alterar aNotifyingThread
classe a ser implementadaRunnable
, precisará alterar parte do seu código que gerencia os Threads, o que é bastante simples de fazer.fonte
notify
método não dentro dorun
método, mas depois dele.Solução usando CyclicBarrier
label0 - barreira cíclica é criada com um número de partes igual ao número de encadeamentos em execução mais um para o encadeamento principal de execução (no qual startDownload () está sendo executado)
label 1 - n-th DownloadingThread entra na sala de espera
a etiqueta 3 - NUMBER_OF_DOWNLOADING_THREADS entrou na sala de espera. O encadeamento principal de execução os libera para começar a fazer os trabalhos de download mais ou menos ao mesmo tempo
etiqueta 4 - o fio principal de execução entra na sala de espera. Esta é a parte 'mais complicada' do código para entender. Não importa qual thread entrará na sala de espera pela segunda vez. É importante que qualquer segmento que entre na sala por último garanta que todos os outros segmentos de download concluam seus trabalhos de download.
A etiqueta 2 - n-ésimo DownloadingThread terminou o trabalho de download e entra na sala de espera. Se for o último, ou seja, NUMBER_OF_DOWNLOADING_THREADS já o inseriu, incluindo o thread principal de execução, o thread principal continuará sua execução somente quando todos os outros threads tiverem terminado o download.
fonte
Você realmente deve preferir uma solução que use
java.util.concurrent
. Encontre e leia Josh Bloch e / ou Brian Goetz sobre o assunto.Se você não estiver usando
java.util.concurrent.*
e estiver assumindo a responsabilidade de usar os Threads diretamente, provavelmente deverájoin()
saber quando um thread será concluído. Aqui está um mecanismo de retorno de chamada super simples. Primeiro estenda aRunnable
interface para ter um retorno de chamada:Em seguida, crie um Executor que executará o seu executável e retornará a ligação quando terminar.
O outro tipo de coisa óbvia a ser adicionada à sua
CallbackRunnable
interface é um meio de lidar com quaisquer exceções; portanto, talvez coloque umapublic void uncaughtException(Throwable e);
linha lá e no seu executor, instale um Thread.UncaughtExceptionHandler para enviar você para esse método de interface.Mas fazer tudo isso realmente começa a cheirar
java.util.concurrent.Callable
. Você realmente deve usarjava.util.concurrent
se o seu projeto permitir.fonte
runner.join()
e, em seguida, o código que desejar depois disso, já que você sabe que o segmento terminou. É apenas que você define esse código como uma propriedade do executável, para poder ter coisas diferentes para diferentes executáveis?runner.join()
é a maneira mais direta de esperar. Eu estava assumindo que o OP não queria bloquear seu segmento de chamada principal, pois eles pediram para serem "notificados" para cada download, o que poderia ser concluído em qualquer ordem. Isso ofereceu uma maneira de ser notificado de forma assíncrona.Deseja esperar que eles terminem? Nesse caso, use o método Join.
Há também a propriedade isAlive se você quiser apenas verificá-la.
fonte
Thread
astart
, o que o encadeamento faz, mas a chamada retorna imediatamente.isAlive
deveria ser um teste simples de flag, mas quando pesquisei no Google o método eranative
.Você pode interrogar a instância do encadeamento com getState (), que retorna uma instância da enumeração Thread.State com um dos seguintes valores:
No entanto, acho que seria um design melhor ter um encadeamento mestre que aguarda a conclusão dos 3 filhos; o mestre continuará a execução quando os outros 3 terminarem.
fonte
Você também pode usar o
Executors
objeto para criar um pool de threads ExecutorService . Em seguida, use oinvokeAll
método para executar cada um de seus threads e recuperar futuros. Isso bloqueará até que todos concluam a execução. Sua outra opção seria executar cada um usando o pool e depois chamarawaitTermination
para bloquear até que o pool termine de executar. Lembre-se de ligar parashutdown
() quando terminar de adicionar tarefas.fonte
Muitas coisas foram alteradas nos últimos 6 anos na frente multi-threading.
Ao invés de usar
join()
e bloquear a API, você pode usar1. API ExecutorService
invokeAll()
2. CountDownLatch
3. ForkJoinPool ou
newWorkStealingPool()
em executores é outra maneira4.Iterar todas as
Future
tarefas de envioExecutorService
e verificação do status com o bloqueio de chamadaget()
noFuture
objetoDê uma olhada nas perguntas relacionadas ao SE:
Como esperar por um thread que gera seu próprio thread?
Executores: como esperar de forma síncrona até que todas as tarefas sejam concluídas se as tarefas forem criadas recursivamente?
fonte
Eu sugeriria olhar o javadoc para a classe Thread .
Você tem vários mecanismos para manipulação de threads.
Seu encadeamento principal poderia
join()
os três encadeamentos em série e não prosseguiria até que todos os três terminassem.Pesquise o estado da rosca gerada em intervalos.
Coloque todos os threads gerados em um separado
ThreadGroup
e faça uma sondagemactiveCount()
noThreadGroup
e espere que ele chegue a 0.Configure um tipo de interface de retorno de chamada ou ouvinte personalizado para comunicação entre threads.
Tenho certeza de que há muitas outras maneiras pelas quais ainda sinto falta.
fonte
Aqui está uma solução simples, curta, fácil de entender e que funciona perfeitamente para mim. Eu precisava desenhar na tela quando outro segmento terminar; mas não foi possível porque o segmento principal tem controle da tela. Assim:
(1) Criei a variável global:
boolean end1 = false;
o thread define como true ao final. Isso é captado no mainthread pelo loop "postDelayed", no qual é respondido.(2) Meu tópico contém:
(3) Felizmente, "postDelayed" é executado no thread principal, então é aí que o outro thread é verificado uma vez a cada segundo. Quando o outro encadeamento termina, isso pode começar o que queremos fazer a seguir.
(4) Finalmente, inicie a coisa toda executando em algum lugar do seu código chamando:
fonte
Eu acho que a maneira mais fácil é usar a
ThreadPoolExecutor
classe.que é exatamente o que precisamos. Substituiremos
afterExecute()
para obter retornos de chamada após cada thread ser concluído e substituiremosterminated()
para saber quando todos os threads forem concluídos.Então aqui está o que você deve fazer
Crie um executor:
E inicie seus tópicos:
O método interno
informUiThatWeAreDone();
faz o que for necessário quando todos os threads estiverem concluídos, por exemplo, atualizar a interface do usuário.NOTA: Não se esqueça de usar
synchronized
métodos, pois você faz seu trabalho em paralelo e MUITO CUIDADO se decidir chamar osynchronized
método de outrosynchronized
método! Isso geralmente leva a impassesEspero que isto ajude!
fonte
Você também pode usar o SwingWorker, que possui suporte interno para alterações de propriedades. Consulte addPropertyChangeListener () ou o método get () para obter um exemplo de ouvinte de mudança de estado.
fonte
Veja a documentação Java da classe Thread. Você pode verificar o estado do thread. Se você colocar os três threads nas variáveis de membro, os três threads poderão ler os estados um do outro.
Você precisa ter um pouco de cuidado, pois pode causar condições de corrida entre os threads. Apenas tente evitar lógica complicada com base no estado dos outros threads. Definitivamente, evite vários threads gravando nas mesmas variáveis.
fonte