Apache Commons RNG 1.7 快速入门与集成示例
以 Apache Commons RNG 1.7 为例,从零创建 Maven 工程引入 commons-rng-simple 与 commons-rng-sampling 依赖,跑通第一个可复现随机数程序,讲清五模块职责、依赖树、与 java.util.Random 及 JDK 17 RandomGenerator 的选型差异和高频坑,命令回显均实测通过。
Apache Commons RNG 1.7 快速入门与集成示例
目标:10 分钟内在 Maven 工程中集成 Apache Commons RNG 1.7,跑通第一个可复现的随机数程序,并讲清模块职责、依赖树与选型边界;本文全部命令与回显均已在 Ubuntu 24.04 + JDK 17 环境实测通过。
Apache Commons RNG 1.7 是 Apache Commons 的伪随机数生成器库,提供 57 种随机源算法与一套分布采样器。它解决的核心痛点是:java.util.Random 算法固定(LCG)、质量有限且无法替换算法,而 Commons RNG 把「算法选择、种子管理、可跳/可分裂并行、分布采样」做成了统一的可插拔 API。本篇聚焦「怎么最快跑起来」:从空目录到可运行的胖 jar;API 细节与算法族对比见本专栏《Apache Commons RNG 1.7 常用 API 与随机源详解》,采样与最佳实践见《Apache Commons RNG 1.7 随机数生成示例与最佳实践详解》。
一、环境信息
本节固定可复现基线,后续命令均在此环境执行。
- 操作系统:Ubuntu 24.04.5 LTS(PRETTY_NAME="Ubuntu 24.04.5 LTS")
- 架构:x86_64
- JDK:OpenJDK 17.0.20.1(Eclipse Temurin 发行版)
- 构建工具:Apache Maven 3.9.16
- 组件版本:Apache Commons RNG 1.7(多模块:client-api、core、simple、sampling、bom)
- Maven 坐标:
org.apache.commons:commons-rng-simple:1.7、org.apache.commons:commons-rng-sampling:1.7 - 工程目录:
/root/rng-quick
环境确认(实时回显):
java -version
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment Temurin-17.0.20.1+1 (build 17.0.20.1+1)
OpenJDK 64-Bit Server VM Temurin-17.0.20.1+1 (build 17.0.20.1+1, mixed mode, sharing)
mvn -version | head -1
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5)
💡 说明:Commons RNG 1.7 的字节码基线是 Java 8(major version 52,实测见本专栏 Ubuntu 安装指南 FAQ Q5),因此工程用 JDK 8 至 21 均可;本文选 JDK 17 是当前 LTS 主流选择。
二、10 分钟最小可用
本节从空目录开始,创建工程、引入依赖、编写验证程序、编译运行,最终得到一个可独立分发的胖 jar。
2.1 创建工程并引入依赖
mkdir -p /root/rng-quick/src/main/java
pom.xml(完整内容,文件路径 /root/rng-quick/pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.rng</groupId>
<artifactId>rng-quick</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-rng-simple</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-rng-sampling</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
<build>
<finalName>rng-quick</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>RngCheck</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
依赖只有两个:commons-rng-simple(RandomSource 枚举工厂,创建生成器的入口)与 commons-rng-sampling(分布采样器,第一个程序不直接用到,后续示例无需改 pom)。用依赖树确认模块间关系:
cd /root/rng-quick && mvn -B dependency:tree | grep -E "rng-quick:jar|commons-rng"
[INFO] com.example.rng:rng-quick:jar:1.0.0
[INFO] +- org.apache.commons:commons-rng-simple:jar:1.7:compile
[INFO] | +- org.apache.commons:commons-rng-client-api:jar:1.7:compile
[INFO] | \- org.apache.commons:commons-rng-core:jar:1.7:compile
[INFO] \- org.apache.commons:commons-rng-sampling:jar:1.7:compile
依赖树解读:simple 传递引入 client-api(接口)与 core(实现),sampling 平级独立——Commons RNG 没有任何第三方传递依赖,整棵树只有它自己的四个模块,集成负担极小。
2.2 编写第一个程序
验证程序 RngCheck.java(文件路径 /root/rng-quick/src/main/java/RngCheck.java):
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.simple.RandomSource;
public class RngCheck {
public static void main(String[] args) {
UniformRandomProvider rng = RandomSource.MT.create(20261015L);
System.out.println("provider = " + rng.getClass().getName());
System.out.println("nextInt() = " + rng.nextInt());
System.out.println("nextInt(100) = " + rng.nextInt(100));
System.out.println("nextLong() = " + rng.nextLong());
System.out.println("nextDouble() = " + rng.nextDouble());
System.out.println("nextBoolean()= " + rng.nextBoolean());
System.out.println("Commons RNG OK");
}
}
代码只有两个 API:RandomSource.MT 是 MersenneTwister 算法的枚举值(1.7 共 57 个可选算法),create(20261015L) 用固定长整型种子创建 UniformRandomProvider。固定种子意味着序列可复现——两次运行输出完全一致,这是随机数库调试与测试的刚需。此处特意用枚举实例方法 MT.create(seed) 而非静态 RandomSource.create(MT, seed):后者在 1.7 已标记 @Deprecated(迁移无行为差异,坑点详见第四章)。
2.3 编译与运行
cd /root/rng-quick && mvn -q -B compile; echo EXIT=$?
EXIT=0
以 classpath 方式运行(四模块 jar 由 Maven 本地仓库提供):
R=$HOME/.m2/repository/org/apache/commons
CP=target/classes:$R/commons-rng-simple/1.7/commons-rng-simple-1.7.jar:$R/commons-rng-core/1.7/commons-rng-core-1.7.jar:$R/commons-rng-client-api/1.7/commons-rng-client-api-1.7.jar:$R/commons-rng-sampling/1.7/commons-rng-sampling-1.7.jar
java -Dfile.encoding=UTF-8 -cp $CP RngCheck
provider = org.apache.commons.rng.core.source32.MersenneTwister
nextInt() = 243253777
nextInt(100) = 79
nextLong() = -7428464460000287899
nextDouble() = 0.86954735697081
nextBoolean()= true
Commons RNG OK
再以 shade 打包并 java -jar 运行(免拼 classpath,适合分发):
mvn -q -B package && ls -l target/*.jar
-rw-r--r-- 1 root root 2852 Oct 15 02:28 target/original-rng-quick.jar
-rw-r--r-- 1 root root 419955 Oct 15 02:28 target/rng-quick.jar
java -Dfile.encoding=UTF-8 -jar target/rng-quick.jar
provider = org.apache.commons.rng.core.source32.MersenneTwister
nextInt() = 243253777
nextInt(100) = 79
nextLong() = -7428464460000287899
nextDouble() = 0.86954735697081
nextBoolean()= true
Commons RNG OK
2.4 验证点与预期结果
| 验证项 | 预期 | 实测 |
|---|---|---|
mvn compile | 退出码 0 | EXIT=0 |
| provider 类名 | org.apache.commons.rng.core.source32.MersenneTwister | 一致 |
| 固定种子下 5 个取数值 | 243253777 / 79 / -7428464460000287899 / 0.86954735697081 / true | 一致 |
| 重复运行输出 | 与首次完全相同(可复现) | 一致 |
rng-quick.jar | 约 420 KB 胖 jar | 419955 字节 |
两次运行(classpath 与 fat jar)输出逐字节相同,固定种子可复现性得到验证。
三、工程集成要点
本节说明把 Commons RNG 集成进既有工程时的模块取舍与基线确认。
3.1 按需选择模块
| 需求 | 依赖 | 说明 |
|---|---|---|
| 基础取数(int/long/double/boolean/bytes) | commons-rng-simple | 传递带出 client-api 与 core,最小依赖 |
| 按分布采样(高斯/指数/泊松等) | 加 commons-rng-sampling | 采样器工厂 Sampler.of(...) |
| 大工程统一版本 | 加 commons-rng-bom(dependencyManagement) | 五模块版本对齐 |
| 直接 new 实现类 | 不建议 | core 官方明确不建议应用访问,接口可能变化 |
3.2 JDK 基线与编码
字节码基线 Java 8(major 52),JDK 8/11/17/21 均可运行;本文 pom 用 maven.compiler.release=17 编译应用代码,若目标环境是 JDK 8 改为 8 即可。含中文源码或输出的环境记得 project.build.sourceEncoding=UTF-8 与运行参数 -Dfile.encoding=UTF-8。
3.3 与既有随机数代码共存
Commons RNG 与 java.util.Random 可同 classpath 共存,互不干扰:前者所有 API 在 org.apache.commons.rng 命名空间下。迁移策略上,新代码用 RandomSource.<算法>.create(seed) 获取 UniformRandomProvider,旧代码继续用 Random,按模块逐步替换;需要 Random 适配层的场景可用 RandomSource 创建后包一层(1.7 的 simple 模块还提供 ThreadLocalRandomSource 用于多线程,实测见本专栏最佳实践篇第五章)。
四、高频坑与修复
本节列出集成阶段最容易踩的四个坑,均附实测复现与修复。
4.1 classpath 缺 core 模块:NoClassDefFoundError
jar 直跑时只放 simple 与 client-api 两个 jar(漏掉 core),实测复现:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/rng/core/source32/JDKRandom
at org.apache.commons.rng.simple.internal.ProviderBuilder$RandomSourceInternal.<clinit>(ProviderBuilder.java:142)
at org.apache.commons.rng.simple.RandomSource.<clinit>(RandomSource.java:189)
at RngCheck.main(RngCheck.java:6)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.rng.core.source32.JDKRandom
修复:client-api、core、simple、sampling 四个模块 jar 必须齐全(或用本文 shade 胖 jar 一劳永逸)。Maven 工程无此问题,传递依赖自动处理。
4.2 使用过时的静态工厂 create:deprecated 警告
静态 RandomSource.create(RandomSource, Object, Object...) 在 1.7 已弃用,编译期触发实测警告:
/root/rng-quick/src/main/java/RngCheck.java:6: warning: [deprecation] create(RandomSource,Object,Object...) in RandomSource has been deprecated
UniformRandomProvider rng = RandomSource.create(RandomSource.MT, 20261015L);
^
1 warning
修复:改用枚举实例方法 RandomSource.MT.create(20261015L)。实测同种子下两种写法输出完全一致(首个 nextInt 均为 243253777),可放心迁移。
4.3 中文输出乱码
POSIX locale 下 JVM 默认 ASCII 编码,中文变问号。修复:运行加 -Dfile.encoding=UTF-8,或系统 export LANG=C.UTF-8;编译期由 pom 的 project.build.sourceEncoding=UTF-8 保证。
4.4 TWO_CMRES_SELECT 无参创建失败
57 个枚举值中,RandomSource.TWO_CMRES_SELECT 的 create() 会抛 IllegalArgumentException: wrong number of arguments——它是 TwoCmres 算法的自选子循环变体,必须显式传入子周期选择参数;普通用途直接用 TWO_CMRES。其余 56 个算法均可无参或带种子直接创建(全量枚举清单见本专栏 API 详解篇第四章)。
五、选型建议
本节回答「什么时候该用 Commons RNG」,与 JDK 自带能力做正面对比。
| 维度 | java.util.Random | JDK 17 RandomGenerator | Apache Commons RNG 1.7 |
|---|---|---|---|
| 算法数量 | 1 种(48 位 LCG) | 数种(L128X128Mix、Xoshiro、SplitMix 等) | 57 种(MT、Xoshiro、PCG、LXM、Philox 等) |
| 算法可插拔 | 否 | 接口统一,实现有限 | 枚举全量可枚举、可编程切换 |
| 可跳/可分裂(并行) | 否 | 部分实现 | Jumpable/LongJumpable/Splittable/ArbitrarilyJumpable 完整体系 |
| 分布采样 | 无 | 无 | 内置 40+ 采样器(高斯/泊松/伽马/Dirichlet 等) |
| 状态保存/恢复 | 无 | 无 | saveState/restoreState 原生支持 |
| 最低 JDK | 1.1 | 17 | 8 |
| 序列跨平台一致 | 是 | 是 | 是(本文实测 Ubuntu/CentOS 输出一致) |
选型建议:简单非关键场景用 java.util.Random 足矣;JDK 17 起新代码可优先 RandomGenerator 家族;需要算法多样性、统计质量、可跳/可分裂并行、分布采样或 Java 8 基线时,Commons RNG 是成熟选择。加密安全随机(密钥、令牌)始终用 SecureRandom,任何伪随机库都不替代它。
六、参考链接
- Apache Commons RNG 用户指南:https://commons.apache.org/proper/commons-rng/userguide/rng-userguide.html
- Commons RNG 1.7 Javadoc(RandomSource/UniformRandomProvider):https://commons.apache.org/proper/commons-rng/javadocs/api-1.7/
- Commons RNG 1.7 发布说明:https://downloads.apache.org/commons/rng/RELEASE-NOTES.txt
- Maven 中央仓库坐标:https://repo1.maven.org/maven2/org/apache/commons/commons-rng-simple/
- 本专栏姊妹篇:《Ubuntu 22.04 安装 Apache Commons RNG 指南》《Apache Commons RNG 1.7 常用 API 与随机源详解》《Apache Commons RNG 1.7 随机数生成示例与最佳实践详解》