为什么ThreadLocalRandom实现如此奇怪? [英] Why is ThreadLocalRandom implemented so bizarrely?

查看:203
本文介绍了为什么ThreadLocalRandom实现如此奇怪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题涉及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 in Random 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 uses Unsafe to access the variables in Thread, which I suppose is because the two classes are in different packages yet the state variables must be private in Thread - Unsafe is only necessary because of the encapsulation violation;
  • ThreadLocalRandom stores its next nextGaussian in a static ThreadLocal instead of in an instance variable as Random 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 in Random verbatim and others with minor modifications; surely much of this code could have been reused.

不幸的是,因为随机 已同步。如果调用它们 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 the ThreadLocalRandom, 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 uses Unsafe to access the variables in Thread, which I suppose is because the two classes are in different packages yet the state variables must be private in Thread - 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 next nextGaussian in a static ThreadLocal instead of in an instance variable as Random 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屋!

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