从Java 8开始有没有理由写`new Random()`? [英] Is there any reason to write `new Random()` since Java 8?

查看:106
本文介绍了从Java 8开始有没有理由写`new Random()`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

出于某种原因,我曾经认为 java.util.Random 是线程不安全的,a-la HashMap BitSet Math.random()实现为包装访问随机 with synchronized block,或 ThreadLocalRandom.current()。nextDouble()

For some reason I used to think that java.util.Random is thread-unsafe, a-la HashMap or BitSet, and Math.random() is implemented either as wrapping access to Random with synchronized block, or ThreadLocalRandom.current().nextDouble().

实际上,事实证明 java.util.Random 是线程安全的(通过atomics)。因此,即使我需要在单个线程中进行一些随机输入,使用 ThreadLocalRandom 是有意义的,因为内部没有原子读写,编译为锁定指令和发出内存障碍。

Actually it turns out that java.util.Random is thread-safe (via atomics). Hence the takeaway: even if I need some random input in a single thread, it makes sense to use ThreadLocalRandom, because there isn't atomic reads and writes inside, compiled as locked instructions and emitting memory barriers.

此外,由于Java 8, ThreadLocalRandom 本质上是一个单例,其状态保持不变在 java.lang.Thread 类的某些字段中。因此,方法 ThreadLocalRandom.current()不能访问 ThreadLocalMap ,但只是一个静态字段读取,i。即非常便宜。

Moreover, since Java 8, ThreadLocalRandom is essentially a singleton, its state is kept in some fields of java.lang.Thread class. Therefore method ThreadLocalRandom.current() is not an access to ThreadLocalMap, but just a static field read, i. e. very cheap.

我有两个问题:


  1. 来自电脑从科学的角度来看,几个线性同余随机生成器的输出(初始化方式 ThreadLocalRandom s是)与单线性同余随机生成器的输出相同随机 ( java.util.Random 实例)?

  1. From Computer Science point of view, is the output of several linear congruential random generators (initialized the way ThreadLocalRandoms are) is same "random" as an output of the single linear congruential random generator (the java.util.Random instance)?

如果对第一个问题的答案是肯定的,有任何的理由来编写构造 new Random()(没有种子)而不是 ThreadLocalRandom.current()曾经?

If answer to the first question is Yes, is there any reason to write the construction new Random() (without seed) instead of ThreadLocalRandom.current(), ever?

更新。我认为像 ThreadLocalRandom.current()。ints()。parallel()。collect(...)之类的调用可能不正确,因为Thread的随机生成器状态可能不是在 ForkJoinPool 工作线程中初始化,但似乎 ThreadLocalRandom 覆盖方法 ints() longs() double(),使上述结构正确。

Update. I supposed that calls like ThreadLocalRandom.current().ints().parallel().collect(...) could be incorrect, because Thread's random generator state might be not initialized in ForkJoinPool worker threads, but appears that ThreadLocalRandom overrides methods ints(), longs() and doubles(), making the above construction correct.

推荐答案


1 ...

1...

这取决于实现,但对于Java,它将是相同 并不坏,因为Java有一个每次操作的静态唯一种子原子长度随机创建。但是我不会对其他语言或实现情况感到惊讶,事实并非如此,他们可能只使用系统时间(Java也使用系统时间,但组合使用唯一种子)。这是在某些系统上,您可以为多个线程获得相同的种子。

It depends on implementation but for Java it will be the same not as bad because Java has a static unique seed atomic long that is manipulated everytime a Random is created. However I would not be surprised in other languages or implementations this is not the case and they might just use the system time (Java uses the system time as well but uses the unique seed in combination). That is on some systems you could get the same seed for multiple threads.

经过进一步检查和一些实际测试(虽然是脆弱的测试),看来我可能有在之前是错误的,因为实际上更糟糕的是使用很多(我说的是100k)同时的随机数生成器(即使它们是不同的实例)。我不完全确定它的种子冲突还是实际全球种子增量变得可预测的事实。当然这可能只是我的测试工具或方法。

After further examination and some actual testing (albeit brittle testing) it appears I might have been wrong before in that it is actually worse to use many (I'm talking 100k) Random number generators at the same time (even though they are different instances). I'm not entirely sure if its seed collision or just the fact that the actual global seed incrementing becomes predictable. Of course this could just be my testing harness or methodology.

根据维基百科:


随机数生成器,尤其是并行计算机,不应该被信任。[12]强烈建议使用多个RNG检查模拟结果,以检查是否引入了偏差。在并行计算机上使用的推荐发生器包括使用序列分裂的组合线性同余生成器和使用独立序列的滞后Fibonacci生成器。

所以理论上它应该更好,因为ThreadLocalRandom会创建独立的序列,所以我的测试可能存在缺陷。

So in theory it is supposed to be better since ThreadLocalRandom would create independent sequences so maybe my testing is flawed.

这当然是基于伪随机。

物理随机性或基于实际熵的安全随机生成器可能会导致差异(即更多/更少的熵),但我不是专家,我不喜欢可以访问一个。

Physical randomness or a secure random generator based on actually entropy might result in differences (ie more/less entropy) but I'm not an expert and I don't have access to one.


2 ...

2...

我无法想出一个特定的用例,但可能是你使用了一个不断创建和处理线程的ExecutorService(假设他们无法控制这个)但从来没有多次(即最多2个并发线程)。您可能会发现ThreadLocalRandom更昂贵,而不是创建单个共享随机数。

I can't come up with a particular use case but one might be that you use an ExecutorService that constantly creates and disposes of threads (assume they don't have control of this) but never many at once (ie max 2 concurrent threads). You might find the ThreadLocalRandom to be more expensive instead of creating a single shared Random.

另外一个原因,可能更好的理由是您可能想要重置种子适用于所有流程。如果你有一个使用线程的游戏(不是很多,但是让我们假装)你可能想要全局重置种子用于测试目的,使用AtomicReference到Random要比尝试将消息传递给所有正在运行的线程容易得多。

Another reason and probably better reason given your comments is that you might want to reset the seed for all processes. If you have a game that uses threads (not many do but lets pretend) you might want to global reset the seed for testing purposes which is far easier with an AtomicReference to a Random than trying to message pass to all the running threads.

您可能不想使用ThreadLocalRandom的另一个原因是平台原因。某些平台对线程创建有特定要求,因此对线程局部创建有特定要求。因此,要解决你有一个更大的问题,而不是randoms ,请查看 Google Apps 其中:

Another reason you might not want to use ThreadLocalRandom are platform reasons. Some platforms have specific requirements on thread creation and thus threadlocal creation. Thus to address "you have a bigger problem, than randoms" check out Google Apps where:


Java应用程序可以创建新线程,但对如何执行此操作有一些限制。这些线程不能创建创建它们的请求寿命。 (在后端服务器上,应用程序可以生成后台线程,这个线程可以创建创建它的请求。

A Java application can create a new thread, but there are some restrictions on how to do it. These threads can't "outlive" the request that creates them. (On a backend server, an application can spawn a background thread, a thread that can "outlive" the request that creates it.)

并解决您为什么要使用无法重用线程的ExecutorService的额外注释:

And to address your additional comment of why would you use an ExecutorService that can't reuse threads:


或使用返回的工厂对象带有ExecutorService的com.google.appengine.api.ThreadManager.currentRequestThreadFactory()(例如,调用Executors.newCachedThreadPool(factory))。

or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., call Executors.newCachedThreadPool(factory)).

即一个不一定重用线程的ThreadPool。

ie a ThreadPool that doesn't necessarily reuse threads.

这篇关于从Java 8开始有没有理由写`new Random()`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆