O método Thread.join () em Java

O método Thread.join () em Java

 

1. Visão geral

Neste tutorial, discutiremos os diferentes métodosjoin() na classeThread. Entraremos em detalhes sobre esses métodos e alguns exemplos de código.

Comowait()enotify() methods,join() é outro mecanismo de sincronização entre threads.

Você pode dar uma olhada rápida emthis tutorial para ler mais sobrewait()enotify().

2. O MétodoThread.join()

O método de junção é definido na classeThread:

public final void join () lança InterruptedException Aguarda que este segmento morra.

Quando invocamos o métodojoin() em um thread, o thread de chamada entra em um estado de espera. Ele permanece em um estado de espera até que o encadeamento referenciado termine.

Podemos ver esse comportamento no seguinte código:

class SampleThread extends Thread {
    public int processingCount = 0;

    SampleThread(int processingCount) {
        this.processingCount = processingCount;
        LOGGER.info("Thread Created");
    }

    @Override
    public void run() {
        LOGGER.info("Thread " + this.getName() + " started");
        while (processingCount > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                LOGGER.info("Thread " + this.getName() + " interrupted");
            }
            processingCount--;
        }
        LOGGER.info("Thread " + this.getName() + " exiting");
    }
}

@Test
public void givenStartedThread_whenJoinCalled_waitsTillCompletion()
  throws InterruptedException {
    Thread t2 = new SampleThread(1);
    t2.start();
    LOGGER.info("Invoking join");
    t2.join();
    LOGGER.info("Returned from join");
    assertFalse(t2.isAlive());
}

Deveríamos esperar resultados semelhantes aos seguintes ao executar o código:

INFO: Thread Created
INFO: Invoking join
INFO: Thread Thread-1 started
INFO: Thread Thread-1 exiting
INFO: Returned from join

The join() method may also return if the referenced thread was interrupted. Nesse caso, o método lança umInterruptedException.

Finalmente,if the referenced thread was already terminated or hasn’t been started, the call to join() method returns immediately.

Thread t1 = new SampleThread(0);
t1.join();  //returns immediately

3. MétodosThread.join() com tempo limite

O métodojoin() continuará esperando se o thread referenciado estiver bloqueado ou estiver demorando muito para ser processado. Isso pode se tornar um problema, pois o encadeamento de chamada não responde. Para lidar com essas situações, usamos versões sobrecarregadas do métodojoin() que nos permitem especificar um período de tempo limite.

Existem doistimed versions que sobrecarregam o métodojoin():

“public final void join(long millis_) lança InterruptedException_Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.”

“public final void join(long millis, int nanos_) lança InterruptedException_Waits at most millis milliseconds plus nanos_ nanossegundos para este segmento morrer. ”_

Podemos usar ojoin() cronometrado conforme abaixo:

@Test
public void givenStartedThread_whenTimedJoinCalled_waitsUntilTimedout()
  throws InterruptedException {
    Thread t3 = new SampleThread(10);
    t3.start();
    t3.join(1000);
    assertTrue(t3.isAlive());
}

Nesse caso, o encadeamento de chamada aguarda aproximadamente 1 segundo para o encadeamento t3 terminar. Se o thread t3 não terminar neste período de tempo, o métodojoin() retorna o controle para o método de chamada.

Timedjoin() depende do sistema operacional para o tempo. Portanto, não podemos assumir quejoin() esperará exatamente o tempo especificado.

4. MétodosThread.join() e sincronização

Além de esperar até o término, chamar o métodojoin() tem um efeito de sincronização. join() creates a happens-before relationship:

“Todas as ações em um encadeamento acontecem - antes que qualquer outro encadeamento retorne com sucesso de um join () nesse encadeamento.”

Isso significa que quando um encadeamento t1 chama t2.join (), todas as alterações feitas por t2 ficam visíveis em t1 no retorno. No entanto, se não invocarmosjoin() ou usarmos outros mecanismos de sincronização, não temos nenhuma garantia de que as alterações no outro encadeamento ficarão visíveis para o encadeamento atual, mesmo se o outro encadeamento for concluído.

Portanto, embora a chamada do métodojoin() para uma thread no estado finalizado retorne imediatamente, ainda precisamos chamá-la em algumas situações.

Podemos ver um exemplo de código sincronizado incorretamente abaixo:

SampleThread t4 = new SampleThread(10);
t4.start();
// not guaranteed to stop even if t4 finishes.
do {

} while (t4.processingCount > 0);

Para sincronizar corretamente o código acima, podemos adicionart4.join() com tempo dentro do loop ou usar algum outro mecanismo de sincronização.

5. Conclusão

O métodojoin() é bastante útil para sincronização entre threads. Neste artigo, discutimos os métodosjoin() e seu comportamento. Também revisamos o código usando o métodojoin().

Como sempre, o código-fonte completo pode ser encontradoover on GitHub.