关于多线程:Java中并发随机数的生成

Concurrent random number generation in Java

在Web服务器上,许多线程正在向客户机提供内容。A/B测试是在网站上执行的,因此我们需要一个prng来为每个会话和测试选择一个变量。显然,当使用一个prng实例时,它是并发访问的,因此可能需要适当的锁定或其他机制。

最初我们使用了java.util.Random(jur),但由于它存在上述缺陷,例如java.util.random有多好,所以我们尝试使用mersennetwester。然而,由于Mersenne Twister依赖于内部状态,因此性能下降,因此需要同步访问nextInt()。另一种选择可能是xor-shift prng,但它与mersenne twister有相同的问题。您可以在以下位置找到解释:http://xorshift.di.unimi.it/

Random使用了compareAndset操作,该操作似乎更快,因为它不需要锁定,但根据javadoc类,它仍然不是线程安全的。相反,建议使用ThreadLocalRandom,这基本上会导致一个prng池。根据请求,一个随机可用线程处理https请求,因此从一组可用prng中选择一个随机prng。显然这非常快。

从这样一个池中生成的随机数是否与从单个prng实例中生成的随机数一样好?

另一种方法是使用单个prng实例从中预生成值流,例如使用ArrayBlockingQueue

哪种解决方案在性能方面更有效?


通过将结果传递给BlockingQueue,可以使任意随机数生成器线程安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SafeRandom implements Runnable {

    Random r = new Random();
    BlockingQueue<Double> q = new ArrayBlockingQueue<>(10);

    double get() throws InterruptedException {
        return q.take();
    }

    @Override
    public void run() {
        try {
            while (true) {
                q.put(r.nextDouble());
            }
        } catch (InterruptedException ie) {
        }
    }

}

为了避免同步问题,每个线程都有一个RNG。为了避免特定于线程的RNG给出相同的输出,让主RNG为特定于线程的RNG生成一系列初始种子。这可能需要向代码中传递一个额外的种子参数来生成一个新线程。

您将需要测试自己在套件上运行RNG的不同选项的速度。如果需要,可以为主RNG和特定于线程的RNG使用不同的RNG引擎。一般来说,为特定于线程的RNG选择具有快速设置时间的RNG。这对主RNG来说并不重要,因为它只设置了一次。