ZetCode

Java ThreadLocalRandom 类

上次修改:2025 年 4 月 26 日

ThreadLocalRandom 类是 Java 并发实用程序的一部分,它生成针对多线程环境优化的伪随机数。 它提供了高效的方法来生成随机整数、双精度浮点数和长整数。

随机数生成在并发应用程序(如游戏、模拟和负载测试)中至关重要。 ThreadLocalRandom 通过为每个线程维护单独的随机数生成器来避免竞争。

ThreadLocalRandom 类概述

ThreadLocalRandom 在 Java 7 中引入,是一个高性能的随机数生成器,专为并发应用程序而设计。 与 Random 相比,由于共享状态,Random 可能会在线程之间产生竞争,而 ThreadLocalRandom 通过为每个线程提供其自身隔离的随机数生成器来消除同步开销。 这显著提高了多线程环境中的性能。

可以通过静态方法 ThreadLocalRandom.current 访问,此类提供了各种便捷方法,包括 nextIntnextDoublenextLong 及其支持指定范围的重载版本。 这些方法本质上是线程安全的,因为每个线程管理其自身的 ThreadLocalRandom 实例,从而确保在高并发下的效率和可靠性。

除了并发优势之外,ThreadLocalRandom 简化了在指定范围内生成随机值的过程,使其成为现代多线程应用程序的首选。 它的设计符合 Java 强调为并行处理提供强大而高效的工具的理念。

基本随机数生成

此示例演示了 ThreadLocalRandom 的基本用法,以并发友好的方式生成随机整数、双精度浮点数和长整数。

BasicThreadLocalRandom.java
package com.zetcode;

import java.util.concurrent.ThreadLocalRandom;

public class BasicThreadLocalRandom {

    public static void main(String[] args) {
        
        ThreadLocalRandom random = ThreadLocalRandom.current();
        
        // Generate random integer
        int randInt = random.nextInt();
        System.out.println("Random integer: " + randInt);
        
        // Generate random integer between 0 and 100 (exclusive)
        int randIntRange = random.nextInt(100);
        System.out.println("Random integer (0-99): " + randIntRange);
        
        // Generate random double between 0.0 and 1.0
        double randDouble = random.nextDouble();
        System.out.println("Random double: " + randDouble);
        
        // Generate random long
        long randLong = random.nextLong();
        System.out.println("Random long: " + randLong);
    }
}

该程序使用 ThreadLocalRandom.current 获取特定于线程的随机生成器。 它生成四个随机值,展示了该类的核心方法。

每次运行都会产生不同的结果,因为每个线程的生成器都以唯一的方式播种。 这些值均匀分布,非常适合并发应用程序。

自定义范围内的随机数

此示例展示了如何使用 ThreadLocalRandom 在特定范围内生成随机数,适用于需要有界值的任务。

RandomRange.java
package com.zetcode;

import java.util.concurrent.ThreadLocalRandom;

public class RandomRange {

    public static void main(String[] args) {
        
        ThreadLocalRandom random = ThreadLocalRandom.current();
        int min = 10;
        int max = 20;
        
        // Generate random integer in range [min, max)
        int randInt = random.nextInt(min, max);
        System.out.println("Random int (" + min + "-" + (max-1) + "): " + randInt);
        
        // Generate random double in range [min, max)
        double randDouble = random.nextDouble(min, max);
        System.out.println("Random double (" + min + "-" + max + "): " + randDouble);
        
        // Generate random long in range [min, max)
        long randLong = random.nextLong(min, max);
        System.out.println("Random long (" + min + "-" + (max-1) + "): " + randLong);
    }
}

该程序使用重载方法直接在指定范围内生成数字。 例如,nextInt(min, max) 方法生成从 minmax-1 的整数。

Random 相比,这种方法简化了范围生成,使其对于模拟或游戏机制等并发任务非常有效。

并发随机数生成

此示例说明了在多线程环境中使用 ThreadLocalRandom,突出了其在并发设置中优于 Random 的效率。

ConcurrentRandom.java
package com.zetcode;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentRandom {

    public static void main(String[] args) {

        try (ExecutorService executor = Executors.newFixedThreadPool(4)) {

            for (int i = 0; i < 4; i++) {
                executor.submit(() -> {
                    ThreadLocalRandom random = ThreadLocalRandom.current();
                    System.out.println(Thread.currentThread().getName() +
                            ": " + random.nextInt(100));
                });
            }

            executor.shutdown();
        }
    }
}

我们创建一个线程池并提交使用 ThreadLocalRandom 生成随机数的任务。 每个线程都使用自己的生成器,避免了竞争。

这证明了该类在并发环境中的优势,在并发环境中,多个线程可以生成随机数而无需同步开销。

生成随机流

此示例展示了如何将 ThreadLocalRandom 与 Java 流一起使用来生成随机数序列,非常适合函数式编程。

RandomStreams.java
package com.zetcode;

import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class RandomStreams {

    public static void main(String[] args) {
        
        ThreadLocalRandom random = ThreadLocalRandom.current();
        
        // Generate stream of random integers
        System.out.println("Random integers (limit 5):");
        IntStream intStream = random.ints(5);
        intStream.forEach(System.out::println);
        
        // Generate stream of random integers in range
        System.out.println("\nRandom integers (50-100, limit 5):");
        IntStream rangeStream = random.ints(5, 50, 100);
        rangeStream.forEach(System.out::println);
        
        // Generate stream of random doubles
        System.out.println("\nRandom doubles (limit 5):");
        random.doubles(5).forEach(System.out::println);
    }
}

该程序使用 intsdoubles 方法生成随机整数和双精度浮点数的流,并提供有界范围和大小的选项。

流提供了一种生成随机数的函数式方法,使 ThreadLocalRandom 在并发设置中可用于现代 Java 应用程序。

从列表中随机选择

此示例演示了如何使用 ThreadLocalRandom 从列表中随机选择元素,这在游戏或抽样等并发应用程序中很有用。

RandomSelection.java
package com.zetcode;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class RandomSelection {

    public static void main(String[] args) {

        ThreadLocalRandom random = ThreadLocalRandom.current();
        List<String> items = List.of("Apple", "Banana", "Orange", "Grape", "Mango");

        // Select one random item
        int index = random.nextInt(items.size());
        String randomItem = items.get(index);
        System.out.println("Random item: " + randomItem);

        // Select multiple random items
        System.out.println("Three random items:");
        for (int i = 0; i < 3; i++) {
            index = random.nextInt(items.size());
            System.out.println(items.get(index));
        }
    }
}

我们使用 nextInt 生成列表大小范围内的随机索引,然后检索相应的元素。 该示例包括多次替换选择。

此技术在并发环境中非常有效,因为 ThreadLocalRandom 可确保线程安全的随机数生成,而无需锁定。

性能比较

此示例比较了多线程环境中 RandomThreadLocalRandom 的性能。 它强调了 ThreadLocalRandom 的效率,它专为并发应用程序而设计,优于 Random,后者需要显式处理才能保持线程安全。

PerformanceComparison.java
package com.zetcode;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class PerformanceComparison {

    static final int COUNT = 1000000;

    public static void main(String[] args) {

        // Test Random
        try (ExecutorService executor = Executors.newFixedThreadPool(4)) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 4; i++) {
                executor.submit(() -> {
                    Random random = new Random();
                    for (int j = 0; j < COUNT; j++) {
                        random.nextInt();
                    }
                });
            }
            executor.shutdown();
            if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
                System.err.println("Random tasks did not complete within the timeout.");
            }
            long duration = System.currentTimeMillis() - start;
            System.out.println("Random time: " + duration + "ms");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("Execution was interrupted.");
        }

        // Test ThreadLocalRandom
        try (ExecutorService executor = Executors.newFixedThreadPool(4)) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 4; i++) {
                executor.submit(() -> {
                    ThreadLocalRandom random = ThreadLocalRandom.current();
                    for (int j = 0; j < COUNT; j++) {
                        random.nextInt();
                    }
                });
            }
            executor.shutdown();
            if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
                System.err.println("ThreadLocalRandom tasks did not complete within the timeout.");
            }
            long duration = System.currentTimeMillis() - start;
            System.out.println("ThreadLocalRandom time: " + duration + "ms");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("Execution was interrupted.");
        }
    }
}

在此示例中,我们使用一个固定的线程池来执行使用 RandomThreadLocalRandom 生成随机数的任务。 这些任务在四个线程上并发运行,每个线程生成一百万个随机数。

虽然 Random 类对于单线程应用程序来说已经足够,但在多线程环境中可能会导致竞争,因为每个线程都会创建自己的实例。 另一方面,ThreadLocalRandom 专门为并发应用程序而设计,提供更快、更高效的随机数生成,而无需同步开销。

此外,使用 try-with-resources 确保正确清理 ExecutorService,而 awaitTermination 验证所有任务是否在给定的超时时间内完成,从而提供更强大的线程池执行处理。

生成随机数组

此示例展示了如何使用 ThreadLocalRandom 使用随机数填充数组,这对于并发测试或模拟任务非常有用。

RandomArray.java
package com.zetcode;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class RandomArray {

    public static void main(String[] args) {
        
        ThreadLocalRandom random = ThreadLocalRandom.current();
        int size = 10;
        
        // Generate array of random integers
        int[] intArray = new int[size];
        for (int i = 0; i < size; i++) {
            intArray[i] = random.nextInt(100);
        }
        System.out.println("Random integer array: " + Arrays.toString(intArray));
        
        // Generate array of random doubles
        double[] doubleArray = new double[size];
        for (int i = 0; i < size; i++) {
            doubleArray[i] = random.nextDouble();
        }
        System.out.println("Random double array: " + Arrays.toString(doubleArray));
    }
}

该程序使用 ThreadLocalRandom 创建随机整数和双精度浮点数的数组。 每个元素的生成效率都很高,适合并发环境。

此类数组对于在多线程应用程序中测试算法或模拟数据很有价值,并使用 Arrays.toString 格式化输出。

来源

Java ThreadLocalRandom 文档

本教程全面探讨了 Java ThreadLocalRandom 类,涵盖了并发设置中的基本用法、范围生成、流和性能。 这对于多线程应用程序至关重要。

作者

我是 Jan Bodnar,一位充满激情的程序员,拥有丰富的经验。 自 2007 年以来,我撰写了 1,400 多篇文章和 8 本电子书。 拥有超过 8 年的教学经验,我致力于分享知识并帮助他人学习编程概念。

列出所有Java教程