quarta-feira, 28 de dezembro de 2016

Organizar execução de threads no Java com join

Nessa outra postagem rápida nós vamos mostrar como organizar a execução de Threads no java com join.

Para entender o ponto dessa postagem, imagine que várias threads estão fazendo um processamento e você vai querer somente o valor mais atual desse processamento ou disparar uma ação depois que todas as threads tenham terminado. Bem, uma forma muito simples é usar join, que vai parar a thread atual até que a thread iniciada tenha concluído a execução.

Claro que no exemplo que vamos mostrar isso não é muito útil, pois as threads estão sendo executados de forma sequencial! Mas o exemplo ser para ilustrar como pode ser utilizado esse método. Com certeza que você ao conhecer esse método, fará uso mais criativo e úteis!

Vamos direto ao ponto, então temos a aplicação abaixo onde diversas threads atualizam o mesmo mapa com o tempo em milis atual e queremos saber o valor mais recente.

package org.aprendendojavase.concorrencia_no_mapa;
import java.util.HashMap;
public class AtualizadorMapa {
final static HashMap<String, Long> nossoMapa = new HashMap<>();
static final String CHAVE = "CHAVE";
public static void main(String[] args) throws InterruptedException {
Runnable atualizaMapa = () -> {
long tempoAtual = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " - " + tempoAtual);
nossoMapa.put(CHAVE, tempoAtual);
};
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(atualizaMapa, "Thread " + i);
thread.start();
}
System.out.println("Último valor do mapa: "+ nossoMapa.get(CHAVE));
}
}
package org.aprendendojavase.concorrencia_no_mapa;
import java.util.HashMap;
public class AtualizadorMapaSincronizadoComJoin {
final static HashMap<String, Long> nossoMapa = new HashMap<>();
static final String CHAVE = "CHAVE";
public static void main(String[] args) throws InterruptedException {
Runnable atualizaMapa = () -> {
long tempoAtual = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " - " + tempoAtual);
nossoMapa.put(CHAVE, tempoAtual);
};
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(atualizaMapa, "Thread " + i);
thread.start();
thread.join();
}
System.out.println("Último valor do mapa: "+ nossoMapa.get(CHAVE));
}
}

O código da classe AtualizadorMapa vai dar xabu sim ou com certeza?
Vai sim, terems um resultado como o seguinte:

Thread 1 - 1482988764969
Último valor do mapa: null
Thread 3 - 1482988764969
Thread 2 - 1482988764969
Thread 0 - 1482988764969
Thread 4 - 1482988764969

As vezes vai funcionar. Sim, as vezes as coisas vão sair no lugar, mas nem sempre. Agora imagine a dor de cabeça que implementações concorrentes não dão em produção!

Uma forma simples (inútil, diriam alguns) de resolver é usando o join. Talvez a única vantagem seja, mesmo perdendo a execução paralela, o fato que se uma thread lançar exceção, o problema principal não é afetado e outras threads poderão executar em seguida. Mas lembrem-se, o que temos aqui é um exemplo simples, há muitos possíveis para o join e meu objetivo hoje é somente apresentar esse método. Veja agora o código da classe AtualizadorMapaSincronizadoComJoin e notem que adicionar um join logo após iniciarmos a thread que criamos. Isso garante que a thread principal, a thread da classe, só vai continuar depois que nossa thread terminar o processamento. Como resultado, temos sempre o valor mais atual no mapa após o for...

O nosso objetivo aqui foi somente apresentar esse interessante método. Ah, se você ficou perdido com a forma que declaramos o Runnable ali, veja nosso artigo sobre Java Funcional com Lambdas.

Nenhum comentário:

Postar um comentário