Javaでは、Random、ThreadLocalRandom、SecureRandom の3つの主要なクラスを使って乱数を生成できます。それぞれ異なる用途や性能特性を持ち、適切に使い分けることが重要です。
擬似乱数生成器 Random
java.util.Random は最も基本的な乱数生成クラスで、指定したシード値に基づいて決定論的な数列を生成します。同じシードと呼び出し順序であれば、常に同じ結果が得られます。これは「擬似乱数」と呼ばれ、真のランダム性はありません。
Random gen = new Random(System.currentTimeMillis());
int val = gen.nextInt(100); // 0〜99の整数
デフォルトでは現在時刻をシードとして使用しますが、明示的に固定値を渡すと再現可能なシーケンスが得られます。これはテストやデバッグには便利ですが、セキュリティ用途には不向きです。
並行処理向け ThreadLocalRandom
java.util.concurrent.ThreadLocalRandom は、マルチスレッド環境での性能向上のために設計されたクラスです。内部でThreadLocalを利用し、各スレッドが独立した乱数生成器を持つため、ロック競合が発生しません。
ThreadLocalRandom localRand = ThreadLocalRandom.current();
int boundedInt = localRand.nextInt(5, 20); // 5以上20未満
double boundedDouble = localRand.nextDouble(1.5, 8.5); // 1.5以上8.5未満
このクラスはシードの設定をサポートしておらず、setSeed() を呼ぶと例外がスローされます。これは意図的に再現性を排除し、スレッドごとの独立性を保つためです。
セキュアな乱数 SecureRandom
java.security.SecureRandom は、暗号学的に安全な乱数を生成するためのクラスです。予測困難なエントロピー源(ハードウェアノイズ、システムイベントなど)を活用し、外部からの推測を極力防ぎます。
SecureRandom secureGen = new SecureRandom();
byte[] token = new byte[16];
secureGen.nextBytes(token); // 暗号トークン生成に適している
高負荷時には生成速度が遅くなる場合がありますが、パスワードリセットトークンやセッションID、認証コードなどのセキュリティ関連用途には必須です。
選択の指針
- 一般用途・単一スレッド →
Random - 高スループット・マルチスレッド →
ThreadLocalRandom - セキュリティ要件あり →
SecureRandom
また、Java 8以降ではints()、longs()、doubles()メソッドにより、ストリーム形式で乱数を生成することも可能です。例えば、10個の0〜50の整数を生成するには以下のように書けます:
IntStream randomStream = ThreadLocalRandom.current().ints(10, 0, 50);
randomStream.forEach(System.out::println);