Conceitos Fundamentais
Em sistemas operacionais, um processo representa uma instância de um programa em execução, enquanto uma thread é a unidade mínima de execução dentro de um processo. Cada processo pode conter múltiplas threads, compartilhando recursos como memória e arquivos.
Concorrência refere-se à execução alternada de tarefas em um mesmo intervalo de tempo — típico em CPUs com apenas um núcleo. Já o paralelismo ocorre quando múltiplas tarefas são executadas simultaneamente, exigindo múltiplos núcleos de CPU.
Para identificar quantos núcleos estão disponíveis no sistema atual:
Runtime runtime = Runtime.getRuntime();
int numCores = runtime.availableProcessors();
Implementação de Threads
Existem duas abordagens principais para criar threads em Java: herança da classe Thread ou implementação da interface Runnable.
Herança da Classe Thread
Quando uma classe estende Thread, ela pode ser tratada como uma thread. O método run() deve ser sobrescrito para definir a lógica da thread.
class Worker extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Olá! " + this.getName());
try {
Thread.sleep(1000); // Pausa por 1 segundo
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Recupera interrupção
}
}
}
}
É importante destacar que o método run() não inicia uma nova thread por si só. A verdadeira criação da thread acontece através do método start0(), chamado internamente pelo método start().
Implementação da Interface Runnable
A segunda forma é mais recomendada, pois evita restrições de herança múltipla. Ao implementar Runnable, o objeto não é uma thread diretamente, mas pode ser encapsulado por uma thread.
class WorkerTask implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Saudação: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
Para iniciar essa tarefa como thread:
public class App {
public static void main(String[] args) {
WorkerTask task = new WorkerTask();
Thread workerThread = new Thread(task);
workerThread.start(); // Inicia a execução da thread
}
}
Internamente, a classe Thread utiliza um padrão de proxy estático: ela recebe um objeto Runnable e delega a execução do método run() a ele. Isso permite maior flexibilidade e reuso de código.
Comportamento do Processo
O encerramento da thread principle (main) não implica o fim do processo. O processo só termina quando todas as threads ativas forem finalizadas. Se nenhuma thread for iniciada com start(), o programa pode entrar em bloqueio ou comportamento inesperado.