22. Concorrência e Threads em Java

A concorrência é um conceito fundamental na programação moderna, especialmente em Java. Com o aumento do número de processadores e núcleos em sistemas computacionais, torna-se cada vez mais importante saber como executar tarefas simultaneamente para aproveitar ao máximo os recursos de hardware disponíveis. Neste capítulo, vamos explorar os conceitos de concorrência e threads em Java, mergulhando desde o básico até aspectos avançados.

Fundamentos de Threads

Em Java, uma thread é a menor unidade de execução que pode ser agendada pelo sistema operacional. Uma aplicação Java pode ter várias threads rodando simultaneamente, cada uma cuidando de uma parte diferente do trabalho. Isso permite que programas realizem várias tarefas ao mesmo tempo, como responder a eventos de usuário enquanto realizam cálculos em segundo plano.

Para criar uma thread em Java, existem duas maneiras principais:

  • Extendendo a classe Thread: Crie uma classe que estenda java.lang.Thread e sobrescreva o método run(). Após criar uma instância da sua classe, chame o método start() para executar a thread.
  • Implementando a interface Runnable: Crie uma classe que implemente a interface Runnable e implemente o método run(). Você pode passar uma instância dessa classe para o construtor de Thread e, em seguida, chamar start().

class MinhaThread extends Thread {
  public void run() {
    // Código a ser executado em uma nova thread
  }
}

class MeuRunnable implements Runnable {
  public void run() {
    // Código a ser executado em uma nova thread
  }
}

public class ExemploThread {
  public static void main(String[] args) {
    MinhaThread mt = new MinhaThread();
    mt.start();
    
    Thread t = new Thread(new MeuRunnable());
    t.start();
  }
}

Gestão de Threads

Gerenciar threads manualmente pode ser complexo e propenso a erros. Java fornece um conjunto de ferramentas para ajudar a gerenciar a execução de threads, incluindo métodos para:

  • Sincronização: Palavra-chave synchronized e classes no pacote java.util.concurrent, como Locks, Semaphores e CountDownLatch, ajudam a gerenciar o acesso a recursos compartilhados.
  • Comunicação entre Threads: Métodos como wait(), notify() e notifyAll() são usados para a comunicação entre threads.
  • Gerenciamento de estado: Métodos como interrupt(), join() e isAlive() ajudam a gerenciar o estado e o ciclo de vida das threads.

Problemas de Concorrência

Desenvolver aplicações concorrentes traz desafios únicos, como:

  • Deadlock: Ocorre quando duas ou mais threads estão esperando indefinidamente umas pelas outras para liberar recursos, resultando em um impasse.
  • Starvation: Acontece quando uma ou mais threads são impedidas de avançar devido à monopolização de recursos por outras threads.
  • Condições de corrida: Surgem quando duas ou mais threads acessam um recurso compartilhado simultaneamente e o resultado final depende da ordem de execução das threads.

Para evitar esses problemas, é essencial entender e aplicar técnicas de sincronização e bloqueio adequadas.

Executores e Serviços de Executor

A partir do Java 5, a API de Concorrência introduziu o framework de executores, que simplifica a execução e gerenciamento de threads. Os executores abstraem a criação e gerenciamento de threads, fornecendo um serviço de executor que pode ser usado para executar tarefas de forma assíncrona.

Exemplos de executores incluem:

  • ThreadPoolExecutor: Gerencia um pool de threads reutilizáveis.
  • ScheduledThreadPoolExecutor: Permite a execução de tarefas com atraso ou periodicamente.
  • Executors.newCachedThreadPool: Cria um pool de threads que cria novas threads conforme necessário, mas reutilizará threads previamente construídas quando estiverem disponíveis.

ExecutorService executor = Executors.newFixedThreadPool(4);

executor.execute(new Runnable() {
  public void run() {
    // Tarefa a ser executada
  }
});

executor.shutdown();

Concorrência de Alto Nível

Além dos executores, a API de Concorrência do Java oferece abstrações de alto nível, como:

  • Future e Callable para trabalhar com resultados de tarefas assíncronas.
  • CompletionService para gerenciar um conjunto de tarefas assíncronas.
  • Concurrent Collections como ConcurrentHashMap e BlockingQueue para trabalhar com coleções em ambientes concorrentes.

Boas Práticas de Concorrência

Para escrever programas concorrentes robustos e eficientes em Java, é importante seguir algumas boas práticas:

  • Minimize o escopo de seções críticas usando sincronização apenas onde é estritamente necessário.
  • Evite bloqueios de longa duração para reduzir o risco de deadlock e melhorar a responsividade do aplicativo.
  • Prefira as abstrações de alto nível da API de Concorrência do Java em vez de gerenciar threads e sincronização manualmente.
  • Use coleções concorrentes para gerenciar o acesso a dados compartilhados entre threads.
  • Esteja ciente dos problemas comuns de concorrência, como condições de corrida, deadlocks e starvation, e saiba como evitá-los.

Em resumo, a concorrência em Java é um recurso poderoso que, quando usado corretamente, pode levar a aplicativos mais responsivos e eficientes. No entanto, também introduz complexidade e potenciais armadilhas. Com um entendimento sólido dos conceitos e ferramentas fornecidos pela API de Concorrência do Java, os desenvolvedores podem criar programas concorrentes seguros e eficazes.

Agora responda o exercício sobre o conteúdo:

Qual das seguintes afirmações sobre a criação de threads em Java é correta?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Imagem do artigo Sincronização e bloqueios em Java

Próxima página do Ebook Gratuito:

118Sincronização e bloqueios em Java

5 minutos

Ganhe seu Certificado deste Curso Gratuitamente! ao baixar o aplicativo Cursa e ler o ebook por lá. Disponível na Google Play ou App Store!

Disponível no Google Play Disponível no App Store

+ de 6,5 milhões
de alunos

Certificado Gratuito e
Válido em todo o Brasil

48 mil exercícios
gratuitos

4,8/5 classificação
nas lojas de apps

Cursos gratuitos em
vídeo, áudio e texto