一、项目背景详细介绍

在日常开发和测试场景中,经常需要生成随机数来模拟用户输入、测试边界条件、实现简单游戏或抽奖功能。Java 标准库提供了多种生成随机数的方式,包括 java.util.RandomMath.random() 和更现代的 ThreadLocalRandom。掌握如何在指定范围内生成随机整数,是熟悉随机性、概率和伪随机数生成器(PRNG)原理的重要基础,同时也能帮助开发者在性能与线程安全之间做出合适的权衡。

本项目将以“生成 1 到 100 之间的随机整数”为例,演示多种 Java 实现方式,并对比各自的优缺点、线程安全性以及性能适用场景。


二、项目需求详细介绍

本项目需要实现以下功能和特性:

  1. 生成单个随机数

    • 返回一个范围在 1(含)到 100(含)之间的随机整数。

    • 支持三种实现方式:Math.random()java.util.RandomThreadLocalRandom

  2. 批量生成随机数

    • 接受参数 count,一次性生成指定个数的随机数列表,并返回 List<Integer>int[]

  3. 指定范围生成

    • 将范围参数化,支持自定义最小值 min 与最大值 max,并保证返回值在 [min, max] 范围内。

  4. 并发安全生成

    • 在多线程环境下,确保生成效率与正确性,演示如何使用 ThreadLocalRandomSplittableRandom

  5. 命令行工具模式

    • 提供命令行入口,支持参数:

      • -n, --number:生成单个随机数

      • -c, --count <count>:生成批量随机数

      • -min <min> -max <max>:指定自定义范围

  6. 单元测试覆盖

    • 使用 JUnit 5 对不同实现及边界情况(如 min > maxcount = 0)进行测试,覆盖率 ≥ 90%。

  7. 工程化和可扩展性

    • 抽象成 RandomGenerator 接口,支持插件式添加新算法(如 SecureRandom)。


三、相关技术详细介绍

  1. Math.random()

    • 静态方法,返回 [0.0, 1.0) 范围的 double,通过乘法与强转实现整数范围随机。

    • 简单易用,但在并发环境中仍共享同一个 PRNG,性能和线程安全有限。

  2. java.util.Random

    • 经典伪随机数生成器,可通过种子构造;

    • 线程安全(内部使用原子操作),但在高并发场景下会有竞争;

    • 提供丰富方法:nextInt(bound)nextDouble()nextGaussian() 等。

  3. java.util.concurrent.ThreadLocalRandom

    • Java 7 引入,为多线程优化的 PRNG;

    • 基于线程本地变量,消除多线程竞争,性能远优于 Random

    • 只可在多线程中通过 ThreadLocalRandom.current() 获取。

  4. java.util.SplittableRandom

    • Java 8 引入,提供更高质量的伪随机性和并行流友好性;

    • 支持 split() 方法将生成器分裂,用于并行任务。

  5. 命令行解析

    • Apache Commons CLI:定义、解析参数,生成帮助文档。

  6. 单元测试

    • JUnit 5:测试不同实现在边界和并发场景的正确性和性能。


四、实现思路详细介绍

  1. 接口设计

    • 定义 public interface RandomGenerator { int nextInt(int min, int max); }

    • 三个实现类:

      • MathRandomGenerator:使用 Math.random()

      • UtilRandomGenerator:封装 java.util.Random 实例;

      • ThreadLocalRandomGenerator:使用 ThreadLocalRandom.current()

  2. 单个随机数生成

    • nextInt(min, max) 方法中,将 Math.random() * (max - min + 1) 加上 min 强转为 int

    • Random.nextInt(bound) 基础上偏移;

    • ThreadLocalRandom.current().nextInt(min, max + 1) 直接调用。

  3. 批量生成

    • 提供工具方法 List<Integer> generateBatch(int min, int max, int count, RandomGenerator gen)

    • 使用循环或 IntStream.range(0, count).map(i -> gen.nextInt(min, max)).boxed().collect(...)

  4. 命令行入口

    • Main 类解析 -n-c-min-max 参数;

    • 根据有无 -c 决定调用单个或批量生成;

    • 可选参数 -impl math|util|threadlocal 选择实现。

  5. 并发场景演示

    • 使用多线程池测试 UtilRandomGeneratorThreadLocalRandomGenerator 生成大量随机数,统计耗时并对比。

  6. 单元测试策略

    • 测试在各种范围和值时是否符合边界;

    • 测试 count = 0 返回空集合;

    • 并发测试:多线程同时调用 nextInt 结果无异常、均在范围内。

五、完整实现代码

// 文件:pom.xml
/*
  Maven 项目对象模型,管理依赖与构建
*/
<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</groupId>
  <artifactId>random-demo</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <!-- Commons CLI 用于命令行解析 -->
    <dependency>
      <groupId>commons-cli</groupId>
      <artifactId>commons-cli</artifactId>
      <version>1.4</version>
    </dependency>
    <!-- JUnit 5 用于单元测试 -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <!-- Surefire 插件运行测试 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M5</version>
      </plugin>
    </plugins>
  </build>
</project>

--------------------------------------------------------------------------------

// 文件:src/main/java/com/example/random/RandomGenerator.java
package com.example.random;

/**
 * 随机数生成器接口,定义在[min, max]范围内生成整数的方法
 */
public interface RandomGenerator {
    /**
     * 生成指定范围内的随机整数
     * @param min 下限(包含)
     * @param max 上限(包含)
     * @return 随机整数
     */
    int nextInt(int min, int max);
}

--------------------------------------------------------------------------------

// 文件:src/main/java/com/example/random/MathRandomGenerator.java
package com.example.random;

/**
 * 基于 Math.random() 的实现
 */
public class MathRandomGenerator implements RandomGenerator {
    @Override
    public int nextInt(int min, int max) {
        if (min > max) {
            throw new IllegalArgumentException("min 不能大于 max");
        }
        // Math.random() 返回 [0.0,1.0),转换为 [min,max]
        return min + (int)(Math.random() * (max - min + 1));
    }
}

--------------------------------------------------------------------------------

// 文件:src/main/java/com/example/random/UtilRandomGenerator.java
package com.example.random;

import java.util.Random;

/**
 * 基于 java.util.Random 的实现
 */
public class UtilRandomGenerator implements RandomGenerator {
    private final Random random = new Random();

    @Override
    public int nextInt(int min, int max) {
        if (min > max) {
            throw new IllegalArgumentException("min 不能大于 max");
        }
        // nextInt(bound) 返回 [0,bound),偏移后得到 [min,max]
        return min + random.nextInt(max - min + 1);
    }
}

--------------------------------------------------------------------------------

// 文件:src/main/java/com/example/random/ThreadLocalRandomGenerator.java
package com.example.random;

import java.util.concurrent.ThreadLocalRandom;

/**
 * 基于 ThreadLocalRandom 的实现,适用于多线程场景
 */
public class ThreadLocalRandomGenerator implements RandomGenerator {
    @Override
    public int nextInt(int min, int max) {
        if (min > max) {
            throw new IllegalArgumentException("min 不能大于 max");
        }
        // ThreadLocalRandom.current() 提供高效线程安全生成
        return ThreadLocalRandom.current().nextInt(min, max + 1);
    }
}

--------------------------------------------------------------------------------

// 文件:src/main/java/com/example/cli/Main.java
package com.example.cli;

import com.example.random.*;
import org.apache.commons.cli.*;

import java.util.ArrayList;
import java.util.List;

/**
 * Main 类处理命令行参数,生成随机数
 */
public class Main {
    public static void main(String[] args) {
        Options options = new Options();
        options.addOption("n", "number", false, "生成一个随机数");
        options.addOption("c", "count", true, "生成多个随机数,指定数量");
        options.addOption(null, "min", true, "随机数下限,默认为1");
        options.addOption(null, "max", true, "随机数上限,默认为100");
        options.addOption(null, "impl", true, "实现方式:math|util|threadlocal,默认threadlocal");

        HelpFormatter formatter = new HelpFormatter();
        CommandLineParser parser = new DefaultParser();
        try {
            CommandLine cmd = parser.parse(options, args);
            int min = Integer.parseInt(cmd.getOptionValue("min", "1"));
            int max = Integer.parseInt(cmd.getOptionValue("max", "100"));
            String impl = cmd.getOptionValue("impl", "threadlocal");
            RandomGenerator gen;
            switch (impl) {
                case "math": gen = new MathRandomGenerator(); break;
                case "util": gen = new UtilRandomGenerator(); break;
                default:      gen = new ThreadLocalRandomGenerator();
            }

            // 生成单个随机数
            if (cmd.hasOption("n")) {
                int r = gen.nextInt(min, max);
                System.out.println("随机数: " + r);
            }
            // 生成多个随机数
            else if (cmd.hasOption("c")) {
                int count = Integer.parseInt(cmd.getOptionValue("c"));
                List<Integer> list = new ArrayList<>();
                for (int i = 0; i < count; i++) {
                    list.add(gen.nextInt(min, max));
                }
                System.out.println("随机数列表: " + list);
            }
            // 无效调用,打印帮助
            else {
                formatter.printHelp("java -jar random-demo.jar", options);
            }
        } catch (Exception e) {
            System.err.println("执行异常: " + e.getMessage());
            formatter.printHelp("java -jar random-demo.jar", options);
        }
    }
}

--------------------------------------------------------------------------------

// 文件:src/test/java/com/example/random/RandomGeneratorTest.java
package com.example.random;

import org.junit.jupiter.api.Test;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;

/**
 * 各实现及边界的单元测试
 */
public class RandomGeneratorTest {

    @Test
    void testMathRandomSingle() {
        RandomGenerator gen = new MathRandomGenerator();
        int r = gen.nextInt(1, 100);
        assertTrue(r >= 1 && r <= 100);
    }

    @Test
    void testUtilRandomSingle() {
        RandomGenerator gen = new UtilRandomGenerator();
        int r = gen.nextInt(10, 20);
        assertTrue(r >= 10 && r <= 20);
    }

    @Test
    void testThreadLocalRandomSingle() {
        RandomGenerator gen = new ThreadLocalRandomGenerator();
        int r = gen.nextInt(5, 5);
        assertEquals(5, r);
    }

    @Test
    void testInvalidRange() {
        RandomGenerator gen = new MathRandomGenerator();
        assertThrows(IllegalArgumentException.class, () -> gen.nextInt(10, 1));
    }

    @Test
    void testBatchGeneration() {
        RandomGenerator gen = new UtilRandomGenerator();
        int count = 1000;
        for (int i = 0; i < count; i++) {
            int r = gen.nextInt(1, 100);
            assertTrue(r >= 1 && r <= 100);
        }
    }

    @Test
    void testConcurrencyPerformance() throws InterruptedException {
        RandomGenerator gen1 = new UtilRandomGenerator();
        RandomGenerator gen2 = new ThreadLocalRandomGenerator();
        int threads = 10, per = 10000;
        ExecutorService pool = Executors.newFixedThreadPool(threads);
        // 并发调用,不保证输出,只要不抛异常
        for (int i = 0; i < threads; i++) {
            pool.submit(() -> {
                for (int j = 0; j < per; j++) {
                    int r1 = gen1.nextInt(1, 100);
                    int r2 = gen2.nextInt(1, 100);
                    assertTrue(r1 >= 1 && r1 <= 100);
                    assertTrue(r2 >= 1 && r2 <= 100);
                }
            });
        }
        pool.shutdown();
        assertTrue(pool.awaitTermination(5, TimeUnit.SECONDS));
    }
}

六、代码详细解读

  1. RandomGenerator 接口
    定义 nextInt(int min, int max) 方法,实现不同算法的一致调用方式。

  2. MathRandomGenerator

    • 使用 Math.random(),通过 min + (int)(Math.random() * (max-min+1)) 生成随机数;

    • 适合简单场景,但每次调用都要执行浮点运算,且在多线程环境下使用同一 PRNG 实例。

  3. UtilRandomGenerator

    • 封装 java.util.Random 实例;内部线程安全但在高并发时存在内部锁竞争;

    • 调用 random.nextInt(bound),偏移后得到目标范围。

  4. ThreadLocalRandomGenerator

    • 使用 ThreadLocalRandom.current(),基于线程本地的 PRNG;

    • 消除多线程竞争,性能优于 Random,推荐在多线程场景下使用。

  5. Main 类

    • 使用 Commons CLI 解析 -n-c--min--max--impl 参数;

    • 不带参数时打印帮助;

    • 根据 impl 选择实现类,分别执行单个或批量随机数生成并输出。

  6. RandomGeneratorTest 单元测试

    • 覆盖三种实现的单值生成、边界情况(min==maxmin>max)抛异常;

    • 批量生成测试确保多次调用依然在正确范围;

    • 并发性能测试:创建 10 个线程、每线程 10000 次调用,验证无异常并在合理时间内完成。


七、项目详细总结

本项目通过三种不同方式在 Java 中生成指定范围的随机整数:

  • Math.random():最简单,但性能和并发安全有限;

  • java.util.Random:线程安全,但多线程竞争明显;

  • ThreadLocalRandom:Java 7 引入,基于线程本地变量,性能卓越,推荐并发环境使用。

项目采用接口抽象与多实现的设计模式,命令行工具提供灵活参数配置,JUnit 测试覆盖了边界与并发场景,保证了正确性与可靠性。


八、项目常见问题及解答

Q1:为什么不要直接用 Math.random()?
A:Math.random() 底层共享同一个 Random 实例,多线程时有同步开销,且无法指定种子。

Q2:ThreadLocalRandom 只能在多线程中用吗?
A:可以在单线程中使用,但主要优势在于消除了线程间竞争,在多线程场景性能更好。

Q3:如何生成安全级别的随机数?
A:使用 java.security.SecureRandom,适用于加密场景,但性能相对较低。

Q4:如何生成随机浮点数或 Gaussian 分布?
A:在 RandomThreadLocalRandom 上调用 nextDouble()nextGaussian() 即可。

Q5:如何确保生成的随机序列可重复?
A:对 UtilRandomGeneratorSecureRandom 提供固定种子构造,然后在每次运行时使用相同种子。


九、扩展方向与性能优化

  1. SecureRandom 实现
    添加 SecureRandomGenerator,满足安全场景需求。

  2. SplittableRandom 实现
    引入 SplittableRandom,利用其更优质的随机性和并行友好性。

  3. 并行流批量生成
    使用 IntStream.range(0, count).parallel().map(i -> gen.nextInt(min,max)).toArray() 实现批量并发生成。

  4. 自定义分布
    扩展接口支持不同分布(如正态、泊松),结合 Commons Math 提供更丰富的概率分布。

  5. 可视化与统计
    集成前端或命令行直方图输出,展示生成频率分布情况,验证随机性质量。

  6. 高性能基准测试
    使用 JMH 对三种实现及扩展算法进行微基准测试,量化吞吐量与延迟,为生产环境选型提供数据支撑。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐