09、JDK 17 新特性:增强的伪随机数生成器 JEP 356:灵活强大的随机数生成

以前在 Java 里生成随机数,鹏磊最烦的就是只能用 Random 或者 SecureRandom,算法固定,性能也不太行。JDK 17 的增强伪随机数生成器(Enhanced Pseudorandom Number Generator)终于解决了这个问题,提供了统一的接口和多种实现,让你能根据需求选择合适的算法,性能也提升了不少。

增强的伪随机数生成器是 JEP 356 引入的特性,提供了 RandomGenerator 接口和多个实现类,支持不同的随机数生成算法。这玩意儿让你能根据需求选择合适的算法,比如需要高性能的用 L32X64MixRandom,需要高质量随机性的用 Xoshiro256PlusPlus,需要可分割的用 SplittableRandom

为什么需要增强的随机数生成器

以前用 Random 生成随机数,有几个问题:

  1. 算法固定:只能用线性同余生成器(LCG),质量一般
  2. 性能一般:同步开销大,多线程性能差
  3. 功能有限:不支持流式生成,不支持分割
  4. 扩展性差:不能替换算法实现

增强的随机数生成器解决了这些问题,提供了统一的接口和多种实现。

RandomGenerator 接口

RandomGenerator 是新的统一接口,所有随机数生成器都实现它:

import java.util.random.*;

// 获取默认的随机数生成器
RandomGenerator generator = RandomGenerator.getDefault();  // 获取默认生成器

// 生成随机数
int randomInt = generator.nextInt(100);  // 生成 0-99 的随机整数
long randomLong = generator.nextLong();  // 生成随机 long
double randomDouble = generator.nextDouble();  // 生成 0.0-1.0 的随机浮点数
boolean randomBoolean = generator.nextBoolean();  // 生成随机布尔值

// 生成字节数组
byte[] randomBytes = new byte[16];  // 创建字节数组
generator.nextBytes(randomBytes);  // 填充随机字节

System.out.println("随机整数: " + randomInt);  // 输出随机整数
System.out.println("随机 long: " + randomLong);  // 输出随机 long
System.out.println("随机浮点数: " + randomDouble);  // 输出随机浮点数
System.out.println("随机布尔值: " + randomBoolean);  // 输出随机布尔值

RandomGenerator 接口提供了统一的 API,用起来简单。

不同的实现类

增强的随机数生成器提供了多种实现,每种有不同的特点:

L32X64MixRandom

L32X64MixRandom 是高性能的生成器,适合大多数场景:

import java.util.random.*;

// 创建 L32X64MixRandom 生成器
RandomGenerator generator = RandomGenerator.of("L32X64MixRandom");  // 创建生成器

// 生成随机数
for (int i = 0; i < 10; i++) {
    int value = generator.nextInt(100);  // 生成 0-99 的随机整数
    System.out.print(value + " ");  // 输出随机数
}

L32X64MixRandom 性能好,适合大多数场景。

Xoshiro256PlusPlus

Xoshiro256PlusPlus 是高质量的生成器,适合需要高质量随机性的场景:

import java.util.random.*;

// 创建 Xoshiro256PlusPlus 生成器
RandomGenerator generator = RandomGenerator.of("Xoshiro256PlusPlus");  // 创建生成器

// 生成随机数
for (int i = 0; i < 10; i++) {
    double value = generator.nextDouble();  // 生成随机浮点数
    System.out.print(value + " ");  // 输出随机数
}

Xoshiro256PlusPlus 质量高,适合需要高质量随机性的场景。

SplittableRandom

SplittableRandom 是可分割的生成器,适合并行计算:

import java.util.random.*;

// 创建 SplittableRandom 生成器
SplittableRandom random = new SplittableRandom();  // 创建生成器

// 分割生成器(用于并行计算)
SplittableRandom random1 = random.split();  // 分割生成器 1
SplittableRandom random2 = random.split();  // 分割生成器 2

// 在不同线程使用
new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        int value = random1.nextInt(100);  // 使用生成器 1
        System.out.println("线程1: " + value);  // 输出随机数
    }
}).start();

new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        int value = random2.nextInt(100);  // 使用生成器 2
        System.out.println("线程2: " + value);  // 输出随机数
    }
}).start();

SplittableRandom 可分割,适合并行计算,每个线程用独立的生成器。

流式生成

流式生成是增强随机数生成器的一个亮点,能生成随机数流:

import java.util.random.*;
import java.util.stream.*;

// 创建生成器
RandomGenerator generator = RandomGenerator.getDefault();  // 获取默认生成器

// 生成无限流
IntStream intStream = generator.ints();  // 生成无限整数流
intStream.limit(10).forEach(System.out::println);  // 输出前 10 个

// 生成有限流
IntStream limitedStream = generator.ints(10);  // 生成 10 个随机整数
limitedStream.forEach(System.out::println);  // 输出所有

// 生成指定范围的流
IntStream rangeStream = generator.ints(10, 0, 100);  // 生成 10 个 0-99 的随机整数
rangeStream.forEach(value -> System.out.print(value + " "));  // 输出所有

// 生成浮点数流
DoubleStream doubleStream = generator.doubles(10, 0.0, 1.0);  // 生成 10 个 0.0-1.0 的随机浮点数
doubleStream.forEach(value -> System.out.print(value + " "));  // 输出所有

// 生成 long 流
LongStream longStream = generator.longs(10, 0L, 1000L);  // 生成 10 个 0-999 的随机 long
longStream.forEach(value -> System.out.print(value + " "));  // 输出所有

流式生成用起来方便,配合 Stream API 处理随机数特别爽。

RandomGeneratorFactory

RandomGeneratorFactory 能创建和管理随机数生成器:

import java.util.random.*;

// 获取默认工厂
RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.getDefault();  // 获取默认工厂

// 创建生成器
RandomGenerator generator = factory.create();  // 使用默认种子创建
RandomGenerator seededGenerator = factory.create(12345L);  // 使用指定种子创建

// 获取所有可用的工厂
RandomGeneratorFactory.all()  // 获取所有工厂
    .forEach(f -> {
        System.out.println("名称: " + f.name());  // 输出名称
        System.out.println("组: " + f.group());  // 输出组
        System.out.println("状态位数: " + f.stateBits());  // 输出状态位数
        System.out.println("周期: " + f.period());  // 输出周期
        System.out.println("---");  // 分隔符
    });

// 根据名称创建生成器
RandomGeneratorFactory<RandomGenerator> l32x64Factory = RandomGeneratorFactory.of("L32X64MixRandom");  // 获取工厂
RandomGenerator l32x64Generator = l32x64Factory.create();  // 创建生成器

RandomGeneratorFactory 能查询生成器的属性,选择合适的实现。

实际应用场景

场景一:游戏开发

游戏开发需要大量随机数,增强的随机数生成器特别适合:

import java.util.random.*;

// 游戏随机数生成器
public class GameRandom {
    private final RandomGenerator generator;  // 随机数生成器
    
    public GameRandom() {
        // 使用高性能生成器
        this.generator = RandomGenerator.of("L32X64MixRandom");  // 创建生成器
    }
    
    // 生成随机伤害
    public int randomDamage(int min, int max) {
        return generator.nextInt(min, max + 1);  // 生成 min-max 的随机伤害
    }
    
    // 生成随机掉落
    public boolean randomDrop(double chance) {
        return generator.nextDouble() < chance;  // 根据概率判断是否掉落
    }
    
    // 生成随机位置
    public double[] randomPosition(double minX, double maxX, double minY, double maxY) {
        double x = generator.nextDouble(minX, maxX);  // 随机 X 坐标
        double y = generator.nextDouble(minY, maxY);  // 随机 Y 坐标
        return new double[]{x, y};  // 返回位置
    }
}

// 使用示例
GameRandom gameRandom = new GameRandom();  // 创建游戏随机数生成器

// 生成随机伤害
int damage = gameRandom.randomDamage(10, 20);  // 生成 10-20 的随机伤害
System.out.println("伤害: " + damage);  // 输出伤害

// 生成随机掉落
boolean dropped = gameRandom.randomDrop(0.3);  // 30% 概率掉落
System.out.println("掉落: " + dropped);  // 输出是否掉落

// 生成随机位置
double[] position = gameRandom.randomPosition(0.0, 100.0, 0.0, 100.0);  // 生成随机位置
System.out.println("位置: (" + position[0] + ", " + position[1] + ")");  // 输出位置

游戏开发用增强的随机数生成器,性能好,功能强。

场景二:蒙特卡洛模拟

蒙特卡洛模拟需要大量随机数,流式生成特别适合:

import java.util.random.*;
import java.util.stream.*;

// 蒙特卡洛模拟:计算 π
public class MonteCarloPi {
    private final RandomGenerator generator;  // 随机数生成器
    
    public MonteCarloPi() {
        this.generator = RandomGenerator.getDefault();  // 获取默认生成器
    }
    
    // 计算 π(使用蒙特卡洛方法)
    public double estimatePi(long iterations) {
        // 生成随机点流
        long insideCircle = generator.doubles(iterations)  // 生成 X 坐标流
            .mapToObj(x -> {
                double y = generator.nextDouble();  // 生成 Y 坐标
                return x * x + y * y <= 1.0;  // 判断是否在圆内
            })
            .filter(b -> b)  // 过滤在圆内的点
            .count();  // 统计数量
        
        return 4.0 * insideCircle / iterations;  // 计算 π 的估计值
    }
}

// 使用示例
MonteCarloPi mc = new MonteCarloPi();  // 创建蒙特卡洛模拟器
double pi = mc.estimatePi(1_000_000);  // 使用 100 万次迭代估计 π
System.out.println("π 的估计值: " + pi);  // 输出估计值

蒙特卡洛模拟用流式生成,代码简洁,性能也好。

场景三:数据采样

数据采样是常见需求,流式生成特别适合:

import java.util.random.*;
import java.util.stream.*;
import java.util.*;

// 数据采样器
public class DataSampler {
    private final RandomGenerator generator;  // 随机数生成器
    
    public DataSampler() {
        this.generator = RandomGenerator.getDefault();  // 获取默认生成器
    }
    
    // 随机采样
    public <T> List<T> sample(List<T> data, int size) {
        // 生成随机索引流
        return generator.ints(size, 0, data.size())  // 生成随机索引
            .mapToObj(data::get)  // 获取对应元素
            .collect(Collectors.toList());  // 收集为列表
    }
    
    // 加权采样
    public <T> T weightedSample(Map<T, Double> weights) {
        double totalWeight = weights.values().stream()  // 计算总权重
            .mapToDouble(Double::doubleValue)
            .sum();
        
        double random = generator.nextDouble() * totalWeight;  // 生成随机值
        double cumulative = 0.0;  // 累积权重
        
        for (var entry : weights.entrySet()) {
            cumulative += entry.getValue();  // 累加权重
            if (random <= cumulative) {
                return entry.getKey();  // 返回对应元素
            }
        }
        
        return weights.keySet().iterator().next();  // 返回第一个(不应该到这里)
    }
}

// 使用示例
DataSampler sampler = new DataSampler();  // 创建采样器

// 随机采样
List<String> data = Arrays.asList("A", "B", "C", "D", "E");  // 数据列表
List<String> sample = sampler.sample(data, 3);  // 随机采样 3 个
System.out.println("采样结果: " + sample);  // 输出采样结果

// 加权采样
Map<String, Double> weights = new HashMap<>();  // 权重映射
weights.put("A", 0.5);  // A 的权重 0.5
weights.put("B", 0.3);  // B 的权重 0.3
weights.put("C", 0.2);  // C 的权重 0.2
String weighted = sampler.weightedSample(weights);  // 加权采样
System.out.println("加权采样: " + weighted);  // 输出采样结果

数据采样用流式生成,代码简洁,功能强大。

性能优化

增强的随机数生成器性能优化明显:

import java.util.random.*;
import java.util.*;

// 性能对比
public class RandomPerformance {
    // 传统 Random
    public static void testOldRandom(int iterations) {
        Random random = new Random();  // 创建传统 Random
        long start = System.nanoTime();  // 开始时间
        
        for (int i = 0; i < iterations; i++) {
            random.nextInt();  // 生成随机数
        }
        
        long time = System.nanoTime() - start;  // 计算时间
        System.out.println("传统 Random: " + time / 1_000_000 + " ms");  // 输出时间
    }
    
    // 增强的随机数生成器
    public static void testNewRandom(int iterations) {
        RandomGenerator generator = RandomGenerator.of("L32X64MixRandom");  // 创建生成器
        long start = System.nanoTime();  // 开始时间
        
        for (int i = 0; i < iterations; i++) {
            generator.nextInt();  // 生成随机数
        }
        
        long time = System.nanoTime() - start;  // 计算时间
        System.out.println("增强生成器: " + time / 1_000_000 + " ms");  // 输出时间
    }
    
    // 流式生成
    public static void testStreamRandom(int iterations) {
        RandomGenerator generator = RandomGenerator.getDefault();  // 获取默认生成器
        long start = System.nanoTime();  // 开始时间
        
        generator.ints(iterations).sum();  // 流式生成并求和
        
        long time = System.nanoTime() - start;  // 计算时间
        System.out.println("流式生成: " + time / 1_000_000 + " ms");  // 输出时间
    }
}

// 性能测试
int iterations = 10_000_000;  // 1000 万次迭代
RandomPerformance.testOldRandom(iterations);  // 测试传统 Random
RandomPerformance.testNewRandom(iterations);  // 测试增强生成器
RandomPerformance.testStreamRandom(iterations);  // 测试流式生成

增强的随机数生成器性能提升明显,特别是流式生成的时候。

注意事项和最佳实践

注意事项

  1. 线程安全:大多数实现不是线程安全的,多线程使用需要同步或者用 SplittableRandom

  2. 种子管理:使用相同种子会生成相同的序列,需要随机性时不要用固定种子。

  3. 算法选择:根据需求选择合适的算法,性能和质量要平衡。

  4. 流式生成:流式生成适合批量操作,单个生成用普通方法就行。

最佳实践

  1. 选择合适的算法:根据需求选择合适的算法,性能和质量要平衡。

  2. 使用流式生成:批量生成随机数时用流式生成,性能更好。

  3. 多线程使用 SplittableRandom:多线程场景用 SplittableRandom,每个线程用独立的生成器。

  4. 避免固定种子:需要随机性时不要用固定种子,让系统自动生成。

  5. 文档说明:在代码注释中说明为什么选择某个算法,帮助其他开发者理解。

总结

增强的伪随机数生成器是 JDK 17 的一个实用特性,提供了统一的接口和多种实现,让你能根据需求选择合适的算法。流式生成特别方便,配合 Stream API 处理随机数特别爽。

建议在实际项目中试试,特别是需要大量随机数的场景。下一篇文章咱就聊聊多线程环境下的随机数生成,看看怎么在并发场景下安全高效地生成随机数。兄弟们有啥问题随时问,鹏磊会尽量解答。

本文章最后更新于 2025-11-28