JavaでのThreadLocalRandomのガイド

1概要

ランダムな値を生成することは非常に一般的な作業です。これが、Javaが java.util.Random クラスを提供する理由です。

ただし、このクラスはマルチスレッド環境ではうまく機能しません。

2 ThreadLocalRandom over Random

また、 Randomとは異なり、 ThreadLocalRandom はシードの明示的な設定をサポートしていません。代わりに、 Random から継承された setSeed(long seed) メソッドがオーバーライドされ、呼び出された場合は常に UnsupportedOperationException がスローされます。

3 ThreadLocalRandom を使用した乱数の生成

無制限のランダムな int 値を生成しましょう。

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

上記の例に示すように、 nextLong() メソッドと nextDouble() メソッドを呼び出すことで、 long double の乱数を生成できます。

また、Java 8では、 nextGaussian() メソッドが追加され、ジェネレータのシーケンスからの平均0.0、標準偏差1.0の次の正規分布値が生成されます。

Random クラスと同様に、 doubles()、ints() および longs() メソッドを使用して乱数のストリームを生成することもできます。

4 JMH を使用した ThreadLocalRandom Random の比較

2つのクラスを使用してマルチスレッド環境でランダム値を生成し、JMHを使用してそれらのパフォーマンスを比較する方法を見てみましょう。

まず、すべてのスレッドが__Randomの単一のインスタンスを共有している例を作成しましょう。

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

JMHベンチマークを使用して上記のコードのパフォーマンスを確認しましょう。

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

同様に、プール内のスレッドごとに ThreadLocalRandom のインスタンスを1つ使用する Random インスタンスの代わりに ThreadLocalRandom を使用しましょう。

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

これは、__ThreadLocalRandomを使用した結果です。

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

最後に、 Random ThreadLocalRandom の両方について上記のJMHの結果を比較すると、 Random を使用して1000個の乱数値を生成するのにかかる平均時間は772マイクロ秒であるのに対し、 ThreadLocalRandom を使用すると約625マイクロ秒です。

したがって、 ThreadLocalRandom は、並行性の高い環境ではより効率的であると結論付けられます

  • JMH ** の詳細については、以前のリンク/java-microbenchmark-harness[article here]を参照してください。

5結論

そしていつものように、これらすべての例の実装はhttps://github.com/eugenp/tutorials/tree/master/core-java-concurrency[GitHubプロジェクト]にあります。