java - Java 17 中的线程安全随机生成器

Java 17 添加了一个新的 RandomGenerator 接口(interface)。然而,似乎所有的新实现都不是线程安全的。在多线程情况下使用新接口(interface)的推荐方法是使用 SplittableRandom 并在生成新线程时从原始线程调用 split。但是,在某些情况下,您无法控制生成新线程的代码部分,您只需要在多个线程之间共享一个实例。

我可以使用 Random 但这会导致争用,因为所有的同步。也可以使用 ThreadLocalRandom,但我不愿意这样做,因为这个类现在被认为是“legacy”,而且因为这没有给我一个线程安全的实现RandomGenerator 没有全部样板文件:

 new RandomGenerator() {
    
    @Override 
    public int nextInt() {
      return ThreadLocalRandom.current().nextInt();
    }
    
    @Override
    public long nextLong() {
      return ThreadLocalRandom.current().nextLong();
    }
    
    ...
}

对我来说,这似乎是新 API 中一个相当根本的差距,但我可能遗漏了一些东西。获得 RandomGenerator 的线程安全实现的惯用 Java 17 方法是什么?

最佳答案

当您无法控制线程的工作拆分或创建时,从使用站点的角度来看,最简单的解决方案是 ThreadLocal<RandomGenerator> .

public static void main(String[] args) {
    // spin up threads
    ForkJoinPool.commonPool().invokeAll(
        Collections.nCopies(8, () -> { Thread.sleep(300); return null; }));

    doWork(ThreadLocal.withInitial(synching(SplittableGenerator.of("L32X64MixRandom"))));
    doWork(ThreadLocal.withInitial(synching(new SplittableRandom())));
    doWork(ThreadLocal.withInitial(ThreadLocalRandom::current));
}

static final Supplier<SplittableGenerator> synching(SplittableGenerator r) {
    return () -> {
        synchronized(r) {
            return r.split();
        }
    };
}

private static void doWork(ThreadLocal<RandomGenerator> theGenerator) {
    System.out.println(theGenerator.get().toString());
    Set<Thread> threads = ConcurrentHashMap.newKeySet();
    var ints = Stream.generate(() -> theGenerator.get().nextInt(10, 90))
        .parallel()
        .limit(100)
        .peek(x -> threads.add(Thread.currentThread()))
        .toArray();
    System.out.println(Arrays.toString(ints));
    System.out.println(threads.stream().map(Thread::getName).toList());
    System.out.println();
}

由于这不会在将一个 RNG 移交给另一个线程之前拆分 RNG,而是从已经存在的工作线程中进行拆分,因此它必须同步操作。但是当第一次查询线程局部变量时,每个线程只发生一次。还值得注意的是,基础 RNG 只能从该同步块(synchronized block)访问。

请注意,这也允许集成旧版 ThreadLocalRandom.current()没有额外的同步。它甚至可以与 Random r = new Random(); doTheWork(ThreadLocal.withInitial(() -> r)); 这样的同步 RNG 一起使用.

当然,这只是为了说明,因为所讨论的 RNG 有专门的方法来创建流,这些流可以在工作负载移交给另一个工作线程之前进行拆分。

https://stackoverflow.com/questions/73021220/

相关文章:

java - Maven:如何查找依赖项

awk - 在列中找到确切的字符串

sql - 甲骨文 SQL : SUM in first row with condition

c++ - 优化静态字符串的字符串存储

java - 比较不同时区的 ZoneDdateTime

c++ - 第二次编译时更改随机生成数字的值

java - 如何修复 java.lang.ClassNotFoundException : org

regex - 如果使用 linux 命令行工具在文本文件的下一行中满足条件,则替换换行符

python - 如果我更改了类中的函数名称,该函数会发生什么变化?

c++ - 尽管类型删除,是否可以使用静态多态性(模板)?