重复使用 java.util.Random 个实例与每次都创建一个新实例

Reusing java.util.Random instance vs creating a new instance every time

标题几乎概括了它 - 我们可以创建一个 java.util.Random(或 SecureRandom)的实例,并在每次需要随机值时使用它,或者我们可以每次都创建一个新实例一经请求。想知道哪一个是 首选 方法,为什么?

给出一些关于上下文的想法:随机值是在 HTTP 请求处理程序中生成的,每个请求一个,考虑到 multi-threading,我正在寻找安全性和性能的最佳组合。

视情况而定。

创建单个实例显然更简单,应该是默认行为。 RandomSecureRandom 都是线程安全的,因此可以正常工作。首先做简单而正确的事情,然后根据预期的峰值争用/峰值性能预算衡量您的性能,并分析结果。

Random

如果您使用 Random 并且单实例方法太慢,请考虑尽可能使用 ThreadLocalRandomRandom 中的 JavaDoc 很好地建议了它的用法:

Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs.

它只会为访问它的每个线程创建一个实例。 Random / ThreadLocalRandom 实例的创建成本并不疯狂,但它高于 "normal" 对象的创建,因此您应该避免为每个传入请求创建一个新实例.每个线程创建一个通常是一个不错的最佳选择。

我想说的是,在具有线程池的现代应用程序中,您几乎应该总是使用 ThreadLocalRandom 而不是 Random - 随机性相同,但单线程性能要好得多。

SecureRandom

但是,如果您使用的是 SecureRandom,则 ThreadLocalRandom 不是一个选项。再次强调,不要猜测,要测量!也许使用 SecureRandom 的单个共享实例就足够了。测试你预期的峰值竞争,如果安全随机实例被证明是一个瓶颈,那么才考虑改善这种情况的方法。

创建一个 SecureRandom 实例的成本非常高,因此您绝对不希望为每个传入请求都创建一个实例。

根据您的应用,ThreadLocal<SecureRandom> 可能是一个选项。尽管如此,我认为这是一种矫枉过正,并且类似于 Striped class 的方案(随机创建和访问 X SecureRandom 个实例以帮助防止争用)可能是首选。

如果您需要随机数来保证信息安全,只有加密 RNG(例如 java.security.SecureRandom)才行。对于任何加密 RNG,最简单的方法是只使用它的一个线程安全实例供整个应用程序使用(请注意,根据文档,SecureRandom 是线程安全的);创建密码 RNG 的多个实例通常没有任何好处,因为它们最终都必须使用高熵 ("unpredictable") 数据进行初始化。

Gathering such "unpredictable" data is not trivial,至少对于您的应用程序,当您使用 SecureRandom 时,您不必担心这一点,它主要为您完成此操作,并包含一个 setSeed 方法,您可以用于添加额外数据以补充其随机性。