O que esse código de associação de encadeamento significa?

156

Nesse código, o que as duas junções e quebra significam? t1.join()faz t2parar até t1terminar?

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread t2 = new Thread(new EventThread("e2"));
t2.start();
while (true) {
   try {
      t1.join();
      t2.join();
      break;
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}
user697911
fonte
3
Como ele bloqueará e aguardará o término do encadeamento, por que você usou o loop while?
Mehdi
@MahdiElMasaoudi Suponho que continue esperando mesmo que o thread seja interrompido? Provavelmente não é uma ótima maneira de fazer isso
forresthopkinsa
Lembre-se de aceitar uma resposta aqui, se for útil.
Grey
Como mencionado, você não precisa while(true)chamar o joinmétodo.
Chaklader Asfak Arefe 4/11

Respostas:

311

O que esse código de associação de encadeamento significa?

Para citar o Thread.join()método javadocs :

join() Aguarda que este segmento morra.

Há um encadeamento que está executando seu código de exemplo que provavelmente é o encadeamento principal .

  1. O thread principal cria e inicia os threads t1e t2. Os dois threads começam a ser executados em paralelo.
  2. O encadeamento principal pede t1.join()para aguardar o t1encadeamento.
  3. O t1encadeamento é concluído e o t1.join()método retorna no encadeamento principal. Observe que t1já poderia ter terminado antes que a join()chamada seja feita . Nesse caso, a join()chamada retornará imediatamente.
  4. O encadeamento principal pede t2.join()para aguardar o t2encadeamento.
  5. O t2encadeamento é concluído (ou pode ter sido concluído antes do t1encadeamento) e o t2.join()método retorna no encadeamento principal.

É importante entender que os threads t1e t2estão sendo executados em paralelo, mas o thread principal que os iniciou precisa aguardar o término antes de continuar. Esse é um padrão comum. Além disso, t1e / ou t2poderia ter terminado antes que o thread principal os chamasse join(). Nesse caso, join()não esperará, mas retornará imediatamente.

t1.join() significa fazer com que t2 pare até que t1 termine?

Não. O thread principal que está chamando t1.join()irá parar de executar e aguardará o t1término do thread. O t2encadeamento está sendo executado em paralelo e não é afetado t1nem pela t1.join()chamada.

Em termos de tentativa / captura, os join()arremessos InterruptedExceptionsignificam que o thread principal que está chamando join()pode ser interrompido por outro thread.

while (true) {

Ter as junções em whileloop é um padrão estranho. Normalmente, você faria a primeira junção e, em seguida, a segunda junção, manipulando InterruptedExceptionadequadamente em cada caso. Não há necessidade de colocá-los em um loop.

cinzento
fonte
24
+1 Esse é um padrão muito estranho e provavelmente pode ser removido.
M0skit0
3
Se t1 terminar primeiro, então t2 para terminar. Esse parece ser um processo seqüencial. Um segmento termina primeiro, depois o outro. qual é o objetivo da segmentação múltipla?
user697911
9
Porque o t1e t2pode ser executado em paralelo. É só que maineles precisam terminar antes de continuar. Esse é um padrão típico @ user697911.
Grey
3
O whileloop existe porque (eu acho) ele quer tentar novamente as join()chamadas se uma for interrompida? Eu certamente não escreveria dessa maneira @ user697911.
Grey
5
O loop está lá para garantir que ambos t1e t2terminar. Ou seja. se t1joga o InterruptedException, ele volta e espera t2. Uma alternativa é esperar pelos dois threads em cada Try-Catch, para que o loop possa ser evitado. Além disso, dependendo disso, EventThreadpode fazer sentido fazer dessa maneira, pois estamos executando 2 threads, não um.
Michael Bisbjerg 11/11
68

Esta é uma pergunta favorita da entrevista sobre Java .

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread e2 = new Thread(new EventThread("e2"));
t2.start();

while (true) {
    try {
        t1.join(); // 1
        t2.join(); // 2  These lines (1,2) are in in public static void main
        break;
    }
}

t1.join()significa, t1 diz algo como " Eu quero terminar primeiro ". Mesmo é o caso com t2. Independentemente de quem iniciou t1ou t2encadeava (neste caso, o mainmétodo), o main aguardará t1e t2concluirá sua tarefa.

No entanto, um ponto importante a ser anotado t1e t2eles mesmos podem ser executados em paralelo, independentemente da sequência de chamada de junção em t1e t2. É o main/daemonfio que tem que esperar .

AmitG
fonte
3
Bom exemplo. Sobre "pode ​​funcionar em paralelo": e daí? É importante que o encadeamento principal aguarde PRIMEIRO por t1 e ENTÃO por t2. Realmente não importa o que T1 ou T2 estão fazendo (a partir da perspectiva thread principal)
Alex
1
Você mencionou o segmento "main / daemon". Principal eu entendo, mas o daemon não tem nada a ver com isso. O segmento principal é não daemon.
Cinza
1
t1.join(); t2.join();não permitirá que o thread que executa as junções continue até que ambos os threads sejam finalizados. Na ausência de código muito incomum em outro lugar, a ordem das junções não importa.
19716 David Schwartz
Então t2.join () será chamado somente quando t1 terminar?
Leo Droidcoder 4/17/17
Em outras palavras, se queremos "serializar" a execução dos encadeamentos t1 e t2, precisamos colocar t1.join () logo após t1.start () desde que o encadeamento principal iniciou t1 (e t2 posteriormente) e, é claro, removê-lo de tentar / pegar. Obviamente, ao fazê-lo, a consequência será uma perda de paralelismo.
Dobrávelje 25/05
47

join()significa aguardar a conclusão de um encadeamento. Este é um método bloqueador. Seu encadeamento principal (aquele que faz o join()) aguardará na t1.join()linha até t1concluir seu trabalho e, em seguida, fará o mesmo t2.join().

Avi
fonte
29

Uma imagem vale mais que mil palavras.

    Main thread-->----->--->-->--block##########continue--->---->
                 \                 |               |
sub thread start()\                | join()        |
                   \               |               |
                    ---sub thread----->--->--->--finish    

Espero útil, para mais detalhes clique aqui

xxy
fonte
3
Clara e precisa.
Dobrávelje 25/05
10

Quando o encadeamento tA chama tB.join (), suas causas não apenas esperam que o tB morra ou o tA seja interrompido, mas criam uma relação de ocorrência anterior entre a última instrução em tB e a próxima instrução após tB.join () no segmento tA.

Todas as ações em um encadeamento ocorrem antes que qualquer outro encadeamento retorne com êxito de uma junção () nesse encadeamento.

Significa programa

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        threadB.join();

        while (true) 
            System.out.print(sharedVar);
    }
}

Sempre imprima

>> 1111111111111111111111111 ...

Mas programa

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        // threadB.join();  COMMENT JOIN

        while (true)
            System.out.print(sharedVar);
    }
}

Pode imprimir não apenas

>> 0000000000 ... 000000111111111111111111111111 ...

Mas

>> 00000000000000000000000000000000000000000000 ... 

Sempre apenas '0'.

Como o Java Memory Model não exige 'transferência' do novo valor de 'sharedVar' do threadB para o thread principal sem relação heppens-before (início do thread, associação do thread, uso da palavra-chave 'synchonized', uso de variáveis ​​AtomicXXX, etc.).

Ivan Golovach
fonte
5

Simplificando:
t1.join()retorna após a t1conclusão.
Ele não faz nada para encadear t1, exceto esperar que termine.
Naturalmente, o código a seguir t1.join()será executado somente após t1.join()retornos.

c0der
fonte
1
será executado somente depois que t1.join () retornar +1
mohsen.nour 8/18
3

Da página de documentação do oracle em Junções

O joinmétodo permite que um thread aguarde a conclusão de outro.

Se t1 for um Threadobjeto cuja thread está em execução no momento,

t1.join() : causes the current thread to pause execution until t1's thread terminates.

Se t2 for um Threadobjeto cuja thread está em execução no momento,

t2.join(); causes the current thread to pause execution until t2's thread terminates.

joinAPI é uma API de baixo nível, que foi introduzida em versões anteriores do java. Muitas coisas foram alteradas ao longo de um período de tempo (especialmente com o lançamento do jdk 1.5) na frente da concorrência.

Você pode conseguir o mesmo com a API java.util.concurrent. Alguns dos exemplos são

  1. Usando invokeAll onExecutorService
  2. Usando CountDownLatch
  3. Usando ForkJoinPool ou newWorkStealingPool de Executors(desde java 8)

Consulte as perguntas relacionadas ao SE:

aguarde até que todos os threads concluam seu trabalho em java

Ravindra babu
fonte
1

Para mim, o comportamento do Join () sempre foi confuso, porque eu estava tentando lembrar quem esperaria por quem. Não tente se lembrar dessa maneira.

Por baixo, é puro mecanismo wait () e notify ().

Todos sabemos que, quando chamamos wait () em qualquer objeto (t1), o objeto de chamada (main) é enviado para a sala de espera (estado bloqueado).

Aqui, o thread principal está chamando join () que é wait () sob as cobertas. Portanto, o thread principal aguardará até que seja notificado. A notificação é dada por t1 quando termina sua execução (conclusão do encadeamento).

Após receber a notificação, o main sai da sala de espera e continua sua execução.

Arjun Patil
fonte
0

Espero que ajude!

package join;

public class ThreadJoinApp {

    Thread th = new Thread("Thread 1") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());

            //Thread 2 waits until the thread 1 successfully completes.
            try {
            th.join();
            } catch( InterruptedException ex) {
                System.out.println("Exception has been caught");
            }

            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    public static void main(String[] args) {
        ThreadJoinApp threadJoinApp = new ThreadJoinApp();
        threadJoinApp.th.start();
        threadJoinApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}
Parthasarathy S
fonte
-3

digamos que nosso thread principal inicie os threads t1 e t2. Agora, quando t1.join () é chamado, o encadeamento principal se suspende até o encadeamento t1 morrer e, em seguida, recomeça. Da mesma forma, quando t2.join () é executado, o encadeamento principal se suspende novamente até o encadeamento t2 morrer e, em seguida, retomar.

Então é assim que funciona.

Além disso, o loop while não era realmente necessário aqui.

skmangalam
fonte