0Pricing
Java Academy · Aula

Segurança de threads e efeitos colaterais

Evite estado mutável compartilhado.

Segurança de threads e efeitos colaterais é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

Paralelismo significa acesso concorrente

Em um fluxo paralelo, várias linhas de execução executam suas expressões lambda ao mesmo tempo. Qualquer estado mutável compartilhado que elas acessarem se torna uma condição de corrida.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int safe = IntStream.rangeClosed(1, 1000).parallel().sum();
        System.out.println(safe);
    }
}

O perigo: modificar uma variável compartilhada

Um contador int comum acessado por muitas linhas de execução perde atualizações. Nunca acumule valores em um estado mutável compartilhado dentro de forEach.

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        AtomicInteger counter = new AtomicInteger();
        IntStream.rangeClosed(1, 1000).parallel()
            .forEach(n -> counter.incrementAndGet());
        System.out.println(counter.get());
    }
}

Por que as coleções comuns falham

Adicionar elementos a um ArrayList que não é seguro para linhas de execução a partir de um forEach paralelo pode corrompê-lo ou descartar elementos. A solução é não compartilhar e modificar ao mesmo tempo.

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        List<Integer> result = IntStream.rangeClosed(1, 10).parallel()
            .boxed()
            .collect(Collectors.toList());
        System.out.println(result.size());
    }
}

A ferramenta certa: collect

Use collect em vez de forEach com efeitos colaterais. A estrutura de coletores acumula com segurança contêineres parciais por linha de execução e os combina.

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        List<Integer> squares = IntStream.rangeClosed(1, 6).parallel()
            .map(n -> n * n)
            .boxed()
            .collect(Collectors.toList());
        System.out.println(squares);
    }
}

A ferramenta certa: reduce

reduce é inerentemente seguro para linhas de execução quando a operação é associativa e sem estado, pois combina resultados parciais sem modificar dados compartilhados.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 1000).parallel()
            .reduce(0, Integer::sum);
        System.out.println(sum);
    }
}

Expressões lambda sem estado

As expressões lambda em fluxos devem ser sem estado: o resultado delas depende apenas da entrada, não de variáveis mutáveis externas ou de elementos anteriores.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long count = IntStream.rangeClosed(1, 1000).parallel()
            .filter(n -> n % 2 == 0)
            .count();
        System.out.println(count);
    }
}

Tipos atômicos como alternativa

Se você realmente precisar compartilhar um contador, use classes atômicas, como AtomicLong. Elas serializam as atualizações corretamente, embora adicionem contenção.

import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        AtomicLong sum = new AtomicLong();
        IntStream.rangeClosed(1, 1000).parallel()
            .forEach(sum::addAndGet);
        System.out.println(sum.get());
    }
}

Coleções concorrentes

Quando precisar de um mapa compartilhado entre linhas de execução, um ConcurrentHashMap será seguro. Ainda assim, prefira Collectors.groupingByConcurrent quando possível.

import java.util.Map;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> words = List.of("a", "bb", "cc", "ddd");
        Map<Integer, List<String>> byLen = words.parallelStream()
            .collect(Collectors.groupingByConcurrent(String::length));
        System.out.println(byLen);
    }
}

Evite lógica dependente da ordem

Não dependa da ordem de encontro dentro de expressões lambda paralelas. Operações que pressupõem processamento sequencial produzem resultados incorretos em paralelo.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 100).parallel()
            .map(n -> n * 2)
            .sum();
        System.out.println(sum);
    }
}

Funções puras são paralelizadas com segurança

A regra de ouro é escrever operações puras, sem estado e não interferentes. Assim, um fluxo pode ser sequencial ou paralelo e produzir resultados idênticos e corretos.

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> in = List.of("red", "green", "blue");
        String out = in.parallelStream()
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.joining(","));
        System.out.println(out);
    }
}

Não interferência

Não modifique a coleção de origem enquanto um fluxo a percorre. Isso causa ConcurrentModificationException ou comportamento indefinido, tanto sequencialmente quanto em paralelo.

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Integer> src = List.of(1, 2, 3, 4);
        List<Integer> doubled = src.parallelStream()
            .map(n -> n * 2)
            .collect(Collectors.toList());
        System.out.println(doubled);
    }
}

Verificação rápida

Qual é a maneira recomendada de acumular resultados de um fluxo paralelo?

Recapitulação

Você manteve corretos os fluxos paralelos:

  • O estado mutável compartilhado em expressões lambda paralelas causa condições de corrida.
  • Prefira collect e reduce a forEach com efeitos colaterais.
  • Mantenha as expressões lambda sem estado, puras e não interferentes.
  • Se precisar compartilhar dados, use tipos atômicos ou coleções concorrentes, mas deixe isso como último recurso.

Perguntas Frequentes

A aula “Segurança de threads e efeitos colaterais” é grátis?

Sim — o texto completo de “Segurança de threads e efeitos colaterais” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “Segurança de threads e efeitos colaterais”?

Evite estado mutável compartilhado. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Segurança de threads e efeitos colaterais”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Criando streams paralelos
  2. Quando o paralelismo ajuda
  3. Segurança de threads e efeitos colaterais
  4. Armadilhas comuns
← Voltar para Java Academy