2016-01-02 122 views
1

在Web服务器上,许多线程正在将内容提供给客户端。 A/B测试是在网站上执行的,所以我们需要PRNG为每个会话和测试选择一个变体。 显然,当使用PRNG的单个实例时,它将被同时访问,因此可能需要适当的锁定或其他机制。Java中的并发随机数生成

最初我们使用java.util.Random(juR),但由于它有缺陷提到例如How good is java.util.Random,我们试着用MersenneTwister代替。 然而,由于Mersenne-Twister relies on an internal state这一事实,我们看到性能大幅下降,所以它需要同步nextInt()的访问权限。 另一种选择可能是异或移位PRNG,但它与Mersenne Twister具有相同的问题。 你可以找到一个解释,例如这里:http://xorshift.di.unimi.it/

Randomuses a compareAndSet操作,它似乎更快,因为它不需要锁定,但根据类Javadoc它仍然不是线程安全的。相反,建议使用ThreadLocalRandom,这基本上会导致PRNG池。根据请求,随机可用线程处理HTTPS请求,因此从一组可用的PRNG中选择一个随机PRNG。显然这很快。

从这样的池中产生的随机数与单个PRNG实例中的一样好吗?

另一种方法是使用单个PRNG实例从它预先生成一个值流,例如,通过使用ArrayBlockingQueue

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

+1

对于这个应用程序(选择A或B是否显示给用户),只要它们是均匀分布的,随机数的质量并不重要。 – Henry

+3

您正在预先优化。 Random被记录为线程安全的,并且与Web应用程序所需的所有IO(数据库查询,HTTP请求等)相比,每次启动会话时调用nextBoolean()的性能完全可以忽略不计。 –

+0

这个想法试图在随机数质量方面提出比java.util.Random更好的东西。实际上我们不止有两个变体,所以我们绘制整数。 @Henry据我所读,Random中位数的概率并不是均匀分布的。 – user3001

回答

2

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

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

3

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

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) { 
     } 
    } 

}