Como engenheiro de back-end, enfrentamos situações para processar os dados de forma assíncrona. Hoje vamos ver como isso é feito em java e várias maneiras de fazê-lo.

Thread

O componente básico, porém tão poderoso, da simultaneidade Java é Thread. O Thread de Java está, na verdade, associado ao Thread do Sistema Operacional. A maneira muito básica de criar um Thread é estendendo-o e substituindo o run :

Iniciar a linha faz com que o run() ser chamado.
Você pode perguntar; sim, Thread tem vários outros métodos que podem ser substituídos:

  • Na maioria dos casos, não queremos substituir outros métodos do encadeamento.
  • Uma vez que estendemos o Thread classe, a classe de extensão perde sua capacidade de estender ainda mais, pois Java não suporta heranças múltiplas.
  • Cada thread tem seu próprio objeto quando o estendemos e não é bom para a integridade da memória quando há toneladas de Objects of the extended Thread criada.

Java aborda esses problemas com a interface Runnable. Na verdade, Thread tem um método sobrecarregado que leva Runnable.

Runnable

Runnable é uma interface que possui apenas um método: run(). Sim, Runnable é uma interface funcional e sua instância pode ser criada com a função lambda. No entanto, é uma maneira fácil de fazer isso; para coisas complexas, gostaríamos de implementá-lo. Veja a diferença aqui. É tudo sobre o requisito:

Embora Runnable tenha um run(), não é um Thread, mas apenas uma classe Java até que seja controlado por (passado para) Thread. O início do thread faz com que o objeto executável run() ser chamado.

Sim, o Java resolveu na versão 1.5 e é Callable.

Callable<V>

Callable é uma interface genérica. Por quê? A execução deste comando do valor de retorno como a execução genérica deste comando. Callable também é uma interface funcional e call() é o único método, um método sem argumento que lança Exception e retorna o valor genérico de execução deste comando.

A implementação Callable é muito semelhante ao Runnable:

Veja aqui, a chamada processa os dados e retorna um valor que pode ser coletado após a execução. Mas há uma grande diferença em invocá-lo? Nós usamos ExecutorService para invocar e Future para segurar o resultado. Vamos falar sobre o porquê.

Como você pode ver, não existe um comportamento controlado de criação e execução de Threads (Runnable ou Callable também). Podemos querer controlar o número de threads em execução ao mesmo tempo, pois cada um deles está associado aos threads do sistema operacional. O número de Threads que executamos deve ser menor que o número de núcleos de CPU disponíveis. Todos juntos, Java resolve isso por ExecutorService interface.
Acima está um artigo informativo sobre Programação assíncrona em Java: Parte I.
Sinta-se à vontade para entrar em contato conosco através da seção de comentários, caso tenha alguma dúvida sobre as instruções que compartilhamos acima.