Uma thread é uma linha de execução dentro do seu programa. Por padrão, o Java executa o main numa única thread: uma instrução depois da outra. Com multithreading, várias tarefas avançam ao mesmo tempo — útil para aproveitar todos os núcleos do processador ou para não ficar parado esperando uma resposta de rede ou de banco.
Criando a primeira thread
Runnable tarefa = () -> {
String nome = Thread.currentThread().getName();
for (int i = 1; i <= 3; i++) {
System.out.println(nome + " → passo " + i);
}
};
Thread t1 = new Thread(tarefa, "T1");
Thread t2 = new Thread(tarefa, "T2");
t1.start(); // start(), não run()!
t2.start();
t1.join(); // espera T1 terminar
t2.join();Rode algumas vezes: a ordem das mensagens muda. Isso é normal — quem decide quando cada thread executa é o sistema operacional. E atenção: chamar run() diretamente executa o código na thread atual, sem paralelismo nenhum.
ExecutorService: não crie threads à mão
Criar uma thread nova para cada tarefa é caro. Na prática, usamos um pool de threads que são reaproveitadas:
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
Future<Integer> soma = pool.submit(() -> calcularSoma());
Future<String> relatorio = pool.submit(() -> gerarRelatorio());
System.out.println(soma.get()); // bloqueia até o resultado ficar pronto
System.out.println(relatorio.get());
} // o try-with-resources encerra o pool (Java 19+)O perigo: condição de corrida
Quando duas threads alteram o mesmo dado ao mesmo tempo, o resultado pode ficar errado:
class Contador {
private int valor = 0;
void incrementar() { valor++; } // ler, somar e gravar: 3 passos!
}Com mil threads chamando incrementar(), o total raramente chega a mil. As soluções mais comuns:
// 1. synchronized: só uma thread por vez entra no método
synchronized void incrementar() { valor++; }
// 2. classes atômicas: mais simples e rápidas para contadores
private final AtomicInteger valor = new AtomicInteger();
void incrementar() { valor.incrementAndGet(); }Para coleções, prefira as versões preparadas para concorrência, como ConcurrentHashMap.
Virtual threads (Java 21+)
Desde o Java 21, as virtual threads são threads leves geridas pela própria JVM. Você pode criar milhares delas sem esgotar a memória — ideal para aplicações que passam a maior parte do tempo esperando I/O, como APIs que chamam outros serviços.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
int id = i;
executor.submit(() -> buscarPedido(id)); // uma virtual thread por tarefa
}
}No Spring Boot 3.2 ou superior, basta a propriedade spring.threads.virtual.enabled=true para o servidor web atender as requisições com virtual threads.
ExecutorService em vez de criar threads à mão, proteja o estado compartilhado com synchronized ou classes atômicas e considere virtual threads quando o gargalo for espera de I/O.