为什么ThreadLocalRandom实现如此奇怪? [英] Why is ThreadLocalRandom implemented so bizarrely?
问题描述
此问题涉及OpenJDK版本1.8.0中 ThreadLocalRandom
的实现。
This question regards the implementation of ThreadLocalRandom
in OpenJDK version 1.8.0.
ThreadLocalRandom
提供了一个每线程随机数生成器,没有Random强加的同步开销。最明显的实现(IMO)将是这样的,它似乎保持向后兼容性而没有太多复杂性:
ThreadLocalRandom
provides a per-thread random number generator without the synchronization overhead imposed by Random. The most obvious implementation (IMO) would be something like this, which appears to preserve backward compatibility without much complexity:
public class ThreadLocalRandom extends Random {
private static final ThreadLocal<ThreadLocalRandom> tl =
ThreadLocal.withInitial(ThreadLocalRandom::new);
public static ThreadLocalRandom current() {
return tl.get();
}
// Random methods moved here without synchronization
// stream methods here
}
public class Random {
private ThreadLocalRandom delegate = new ThreadLocalRandom();
// methods synchronize and delegate for backward compatibility
}
但是,实际实现是完全不同的,非常奇怪:
However, the actual implementation is totally different and quite bizarre:
-
ThreadLocalRandom
重复一些方法在随机
逐字和其他略有修改;肯定大部分代码都可以被重用。 -
Thread
存储种子和用于初始化`ThreadLocalRandom的探测变量,违反了封装; -
ThreadLocalRandom
使用Unsafe
来访问<$ c中的变量$ c> Thread ,我想这是因为这两个类在不同的包中,但状态变量必须是私有的Thread
-不安全
只是因为封装违规才有必要; -
ThreadLocalRandom
存储下一个nextGaussian
在静态ThreadLocal
中而不是在实例变量中随机
确实。
ThreadLocalRandom
duplicates some of the methods inRandom
verbatim and others with minor modifications; surely much of this code could have been reused.Thread
stores the seed and a probe variable used to initialize the `ThreadLocalRandom, violating encapsulation;ThreadLocalRandom
usesUnsafe
to access the variables inThread
, which I suppose is because the two classes are in different packages yet the state variables must be private inThread
-Unsafe
is only necessary because of the encapsulation violation;ThreadLocalRandom
stores its nextnextGaussian
in a staticThreadLocal
instead of in an instance variable asRandom
does.
总的来说,我粗略检查似乎显示了一个丑陋的随机
副本没有优于上述简单实现的优点。但是标准库的作者很聪明,所以必须有一些这种奇怪方法的原因。有没有人知道为什么 ThreadLocalRandom
以这种方式实现?
Overall my cursory inspection seems to reveal an ugly copy of Random
with no advantages over the simple implementation above. But the authors of the standard library are smart so there must be some reason for this weird approach. Does anyone have any insight into why ThreadLocalRandom
was implemented this way?
推荐答案
关键问题是许多代码都是遗留的,无法(轻松)更改 - Random
通过同步所有设计为线程安全它的方法。这是有效的,因为 Random
的实例可以在多个线程中使用,但这是一个严重的瓶颈,因为没有两个线程可以同时检索随机数据。一个简单的解决方案是构造一个 ThreadLocal< Random>
对象,从而避免锁争用,但这仍然不理想。即使无争议,构建 n Random
随机>> code>实例在他们基本上都做同样的工作时是浪费的。
The key problem is a lot of the code is legacy and can't (easily) be changed - Random
was designed to be "thread-safe" by synchronizing all its methods. This works, in that instances of Random
can be used across multiple threads, but it's a severe bottleneck as no two threads can simultaneously retrieve random data. A simple solution would be to construct a ThreadLocal<Random>
object thereby avoiding the lock contention, however this still isn't ideal. There's still some overhead to synchronized
methods even when uncontested, and constructing n Random
instances is wasteful when they're all essentially doing the same job.
所以在高级 ThreadLocalRandom
作为性能优化而存在,因此它的实现将是奇怪的,因为JDK开发人员已经花时间优化它。
So at a high-level ThreadLocalRandom
exists as a performance optimization, hence it makes sense that its implementation would be "bizarre", as the JDK devs have put time into optimizing it.
那里JDK中有许多类,乍一看是丑陋的。但请记住,JDK作者正在解决与您不同的问题。他们编写的代码将以数千甚至数百万的开发人员以无数方式使用。他们必须定期权衡最佳实践以提高效率,因为他们编写的代码非常关键。
There are many classes in the JDK that, at first glance, are "ugly". Remember however that the JDK authors are solving a different problem than you. The code they write will be used by thousands if not millions of developers in countless ways. They have to regularly trade-off best-practices for efficiency because the code they're writing is so mission critical.
有效的Java:第55项也解决了这个问题 - 关键点是优化应该由专家作为最后的手段来完成。 JDK开发人员是那些专家。
Effective Java: Item 55 also addresses this issue - the key point being that optimization should be done as a last resort, by experts. The JDK devs are those experts.
针对您的具体问题:
ThreadLocalRandom
复制Random
中的一些方法,以及其他经过微小修改的方法;肯定大部分代码都可以重复使用。
ThreadLocalRandom
duplicates some of the methods inRandom
verbatim and others with minor modifications; surely much of this code could have been reused.
不幸的是,因为随机$上的方法c $ c>
已同步
。如果调用它们 ThreadLocalRandom
会引入随机
的锁定争用问题。 TLR 需要覆盖每个方法,以便从方法中删除 synchronized
关键字。
Unfortunately no, as the methods on Random
are synchronized
. If they were invoked ThreadLocalRandom
would pull in Random
's lock-contention trouble. TLR needs to override every method in order to remove the synchronized
keyword from the methods.
Thread
存储种子和用于初始化 ThreadLocalRandom
的探测变量,违反封装;
Thread
stores the seed and a probe variable used to initialize theThreadLocalRandom
, violating encapsulation;
首先,它实际上不是违反封装,因为该字段仍然是封装私有的。它是由用户封装的,这是目标。我不会对此过于笼罩,因为这里的决定是为了提高性能。有时性能是以正常的良好设计为代价的。实际上,这种违规是无法察觉的。行为只是封装在两个类而不是一个类中。
First off, it's really not "violating encapsulation" since the field is still package-private. It's encapsulated from users, which is the goal. I wouldn't get too hung up on this as the decisions were made here to improve performance. Sometimes performance comes at the cost of normal good design. In practice this "violation" is undetectable. The behavior is simply encapsulated inside two classes instead of a single one.
将种子放入 Thread
允许 ThreadLocalRandom
完全无状态(除了初始化的
字段,这在很大程度上是不相关的),因此只需要一个实例在整个应用程序中存在。
Putting the seed inside Thread
allows ThreadLocalRandom
to be totally stateless (aside from the initialized
field, which is largely irrelevant), and therefore only a single instance ever needs to exist across the whole application.
ThreadLocalRandom
使用不安全的
来访问Thread
中的变量,我想这是因为这两个类在不同的包中,但状态变量必须是线程
-不安全
只是因为封装违规而需要;
ThreadLocalRandom
usesUnsafe
to access the variables inThread
, which I suppose is because the two classes are in different packages yet the state variables must be private inThread
-Unsafe
is only necessary because of the encapsulation violation;
许多JDK类使用 Unsafe
。这是一种工具,而不是罪恶。再一次,我不会对这个事实感到压力过大。该类被称为不安全
,以阻止非专业开发人员滥用它。我们相信/希望JDK作者足够聪明,知道什么时候可以安全使用。
Many JDK classes use Unsafe
. It's a tool, not a sin. Again, I just wouldn't get too stressed out about this fact. The class is called Unsafe
to discourage lay-developers from misusing it. We trust/hope the JDK authors are smart enough to know when it's safe to use.
ThreadLocalRandom
将其下一个nextGaussian
存储在静态ThreadLocal
中,而不是存储在随机
确实。
ThreadLocalRandom
stores its nextnextGaussian
in a staticThreadLocal
instead of in an instance variable asRandom
does.
因为只有一个的实例ThreadLocalRandom
不需要将其作为实例变量。我想你也可以说它不需要它是一个静态
,但那时你只是在讨论风格。至少使它 static
更明确地使该类基本上无状态。正如在文件中提到,这个字段不是必需的,但确保与随机
类似的行为。
Since there will only ever be one instance of ThreadLocalRandom
there's no need for this to be an instance variable. I suppose you could alternatively make the case that there's no need for it to be a static
either, but at that point you're just debating style. At a minimum making it static
more clearly leaves the class essentially stateless. As mentioned in the file, this field is not really necessary, but ensures similar behavior to Random
.
这篇关于为什么ThreadLocalRandom实现如此奇怪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!