Guia para ThreadLocalRandom em Java

Guia para ThreadLocalRandom em Java

1. Visão geral

Gerar valores aleatórios é uma tarefa muito comum. É por isso que Java fornece a classejava.util.Random.

No entanto, esta classe não tem um bom desempenho em um ambiente multi-thread.

De forma simplificada, o motivo do baixo desempenho deRandom em um ambiente multi-threaded é devido à contenção - visto que vários threads compartilham a mesma instânciaRandom.

Para resolver essa limitação,Java introduced the java.util.concurrent.ThreadLocalRandom class in JDK 7 – for generating random numbers in a multi-threaded environment.

Vamos ver o desempenho deThreadLocalRandom e como usá-lo em aplicativos do mundo real.

2. ThreadLocalRandom sobreRandom

ThreadLocalRandom is a combination of ThreadLocal and Random classes, which is isolated to the current thread. Assim, ele atinge melhor desempenho em um ambiente multithread simplesmente evitando qualquer acesso simultâneo aos objetosRandom.

O número aleatório obtido por um encadeamento não é afetado pelo outro encadeamento, enquantojava.util.Random fornece números aleatórios globalmente.

Além disso, ao contrário deRandom,ThreadLocalRandom não oferece suporte para definir a semente explicitamente. Em vez disso, ele substitui o métodosetSeed(long seed) herdado deRandom para sempre lançar umUnsupportedOperationException se chamado.

Vamos agora dar uma olhada em algumas das maneiras de gerar valoresint, longedouble aleatórios.

3. Gerando valores aleatórios usandoThreadLocalRandom

De acordo com a documentação Oracle,we just need to call ThreadLocalRandom.current() method, and it will return the instance of ThreadLocalRandom for the current thread. Podemos então gerar valores aleatórios invocando os métodos de instância disponíveis da classe.

Vamos gerar um valorint aleatório sem nenhum limite:

int unboundedRandomValue = ThreadLocalRandom.current().nextInt());

A seguir, vamos ver como podemos gerar um valorint limitado aleatório, o que significa um valor entre um determinado limite inferior e superior.

Aqui está um exemplo de geração de um valorint aleatório entre 0 e 100:

int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);

Observe que 0 é o limite inferior inclusivo e 100 é o limite superior exclusivo.

Podemos gerar valores aleatórios paralongedouble invocando os métodosnextLong()enextDouble() de maneira semelhante à mostrada nos exemplos acima.

Java 8 também adiciona o métodonextGaussian() para gerar o próximo valor normalmente distribuído com uma média de 0,0 e um desvio padrão de 1,0 da sequência do gerador.

Tal como acontece com a classeRandom, também podemos usar os métodosdoubles(), ints()elongs() para gerar fluxos de valores aleatórios.

4. ComparandoThreadLocalRandom eRandom usando JMH

Vamos ver como podemos gerar valores aleatórios em um ambiente multi-thread, usando as duas classes, e então comparar seu desempenho usando JMH.

Primeiro, vamos criar um exemplo em que todos os threads compartilham uma única instância deRandom. Aqui, estamos enviando a tarefa de gerar um valor aleatório usando a instânciaRandom para umExecutorService:

ExecutorService executor = Executors.newWorkStealingPool();
List> callables = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
    callables.add(() -> {
         return random.nextInt();
    });
}
executor.invokeAll(callables);

Vamos verificar o desempenho do código acima usando o benchmarking JMH:

# Run complete. Total time: 00:00:36
Benchmark                                            Mode Cnt Score    Error    Units
ThreadLocalRandomBenchMarker.randomValuesUsingRandom avgt 20  771.613 ± 222.220 us/op

Da mesma forma, vamos agora usarThreadLocalRandom em vez da instânciaRandom, que usa uma instância deThreadLocalRandom para cada thread no pool:

ExecutorService executor = Executors.newWorkStealingPool();
List> callables = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    callables.add(() -> {
        return ThreadLocalRandom.current().nextInt();
    });
}
executor.invokeAll(callables);

Aqui está o resultado de usarThreadLocalRandom:

# Run complete. Total time: 00:00:36
Benchmark                                                       Mode Cnt Score    Error   Units
ThreadLocalRandomBenchMarker.randomValuesUsingThreadLocalRandom avgt 20  624.911 ± 113.268 us/op

Finalmente, comparando os resultados JMH acima paraRandom eThreadLocalRandom, podemos ver claramente que o tempo médio necessário para gerar 1000 valores aleatórios usandoRandom é 772 microssegundos, enquanto usandoThreadLocalRandom é cerca de 625 microssegundos.

Assim, podemos concluir queThreadLocalRandom is more efficient in a highly concurrent environment.

Para saber mais sobreJMH, verifique nossoarticle here anterior.

5. Conclusão

Este artigo ilustrou a diferença entrejava.util.Random ejava.util.concurrent.ThreadLocalRandom.

Também vimos a vantagem deThreadLocalRandom sobreRandom em um ambiente multithread, bem como o desempenho e como podemos gerar valores aleatórios usando a classe.

ThreadLocalRandom é uma adição simples ao JDK, mas pode criar um impacto notável em aplicativos altamente simultâneos.

E, como sempre, a implementação de todos esses exemplos pode ser encontrada emGitHub project.