Questão nº 13
Questão de Tecnologia da Informação · FGV CVM 2024 (nº 13)
Considere o seguinte código em Java:
import java.util.concurrent.atomic.AtomicInteger;
class MultiSomador extends Thread {
int n = 1_000;
int saida = 1;
AtomicInteger contagem = new AtomicInteger(1);
public void run() {
for (short i = 0; i < n; i++)
saida = contagem.getAndIncrement();
}
}
public class Principal {
public static void main(String[] args)
throws InterruptedException {
MultiSomador a = new MultiSomador();
Thread b = new Thread(a, "b");
Thread c = new Thread(a, "c");
b.start(); b.join(0);
c.start(); c.join(0);
System.out.print(a.contagem+", "+a.saida);
}
}
Ao ser executado em um ambiente Java com suporte à classe de biblioteca AtomicInteger, o código acima imprime na saída padrão:
- A1, 1
- BException
- C1000, 999
- D2000, 1999
- E2001, 2000 (alternativa correta)
Resposta comentada
Gabarito Alternativa E
O conceito-chave aqui é a execução sequencial de threads garantida pelo Thread.join(0) e o uso de um contador atômico (AtomicInteger) para atualizações seguras. AtomicInteger é um tipo de número inteiro que pode ser atualizado por várias partes do programa (threads) ao mesmo tempo sem causar erros, garantindo que cada atualização seja completa e não seja interrompida. Thread.join(0) é um comando que faz o programa principal esperar infinitamente até que a thread que ele está esperando termine completamente sua execução.
- (A) Incorreta: Esta alternativa sugere que o contador e a variável
saidanão foram alterados, o que não ocorre, pois as threads executam os laços de incremento. - (B) Incorreta: Não há condições para que uma exceção seja lançada neste código.
AtomicIntegerfoi projetado para lidar com concorrência de forma segura. - (C) Incorreta: Esta seria a saída se apenas uma das threads executasse e o
AtomicIntegeriniciasse em 0, ou se houvesse uma interpretação incorreta dogetAndIncrement(). No entanto, duas threads executam sequencialmente e o contador inicia em 1. - (D) Incorreta: Esta alternativa seria tentadora se o
AtomicIntegercontagemtivesse sido inicializado com 0 em vez de 1. Secontagemcomeçasse em 0, após 2000 incrementos, seu valor final seria 2000, e o último valor atribuído asaida(que é o valor antes do último incremento) seria 1999. No entanto,contageminicia em 1. - (E) Correta: A explicação detalhada é a seguinte:
contagemé inicializado com 1.saidaé inicializado com 1.- A
mainthread inicia a threadb. b.join(0)faz amainthread esperar até que a threadbtermine completamente.- A thread
bexecuta o laço 1000 vezes. Em cada iteração,saida = contagem.getAndIncrement();.getAndIncrement()retorna o valor atual decontageme depois o incrementa.- Após 1000 iterações de
b:contagem(que começou em 1) terá sido incrementado 1000 vezes, resultando em1 + 1000 = 1001.saidaterá recebido o valor decontagemantes do último incremento deb. O primeirogetAndIncrement()retorna 1, o segundo 2, ..., o milésimo retorna 1000. Então,saidaserá 1000.
- Após
bterminar, amainthread inicia a threadc. c.join(0)faz amainthread esperar até que a threadctermine completamente.- A thread
cexecuta o laço 1000 vezes. Neste ponto,contagemé 1001 esaidaé 1000.- Após 1000 iterações de
c:contagem(que começou em 1001 parac) terá sido incrementado 1000 vezes, resultando em1001 + 1000 = 2001.saidaterá recebido o valor decontagemantes do último incremento dec. O primeirogetAndIncrement()decretorna 1001, o segundo 1002, ..., o milésimo retorna 2000. Então,saidaserá 2000.
- Após 1000 iterações de
- Finalmente, a
mainthread imprime os valores finais:a.contagem(2001) ea.saida(2000).
Fonte: FGV CVM 2024 Analista CVM - TI Sistemas e Desenvolvimento (Perfil 8) (Caderno Tipo 1). Reproduzida para fins de estudo.