这个问题与OpenJDK 1.8.0版中ThreadLocalRandom的实现有关。
ThreadLocalRandom提供了每线程随机数生成器,而没有Random带来的同步开销。最明显的实现(IMO)可能是这样的,它似乎保留了向后兼容性,而没有太多的复杂性:

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
}

但是,实际的实现是完全不同的并且很奇怪:
  • ThreadLocalRandom逐字复制了Random中的某些方法,而其他方法进行了少量修改。当然,这些代码中的大部分都可以被重用。
  • Thread存储种子和用于初始化ThreadLocalRandom的探测变量,这违反了封装;
  • ThreadLocalRandom使用Unsafe来访问Thread中的变量,我想这是因为这两个类位于不同的程序包中,但是状态变量必须在Thread中是私有(private)的-Unsafe仅由于封装违规才需要;
  • ThreadLocalRandom将其下一个nextGaussian存储在静态ThreadLocal中,而不是像Random那样存储在实例变量中。

  • 总的来说,我的粗略检查似乎揭示了Random的丑陋副本,与上面的简单实现相比没有任何优势。但是标准库的作者很聪明,所以这种奇怪的方法一定有其原因。是否有人对为什么以这种方式实现ThreadLocalRandom有任何见识?

    最佳答案

    关键问题是很多代码都是旧代码,无法(轻松)更改- Random 通过同步其所有方法而被设计为“线程安全的”。这是可行的,因为可以在多个线程中使用Random实例,但这是一个严重的瓶颈,因为没有两个线程可以同时检索随机数据。一个简单的解决方案是构造一个ThreadLocal<Random>对象,从而避免锁争用,但是,这仍然不是理想的选择。即使没有争议,synchronized方法仍然有一些开销,并且构造n个Random实例在本质上都完成相同工作时是浪费的。

    因此,存在一个高级ThreadLocalRandom作为性能优化,因此,由于JDK开发人员花了一些时间对其进行优化,因此将其实现称为“bizarre”是有道理的。

    乍一看,JDK中有许多类是“丑陋的”。但是请记住,JDK作者正在解决与您不同的问题。他们编写的代码将由数以千计甚至数百万的开发人员以无数的方式使用。他们必须定期权衡最佳实践以提高效率,因为他们正在编写的代码对任务至关重要。

    Effective Java: Item 55也解决了这个问题-关键是专家应该在最后手段下进行优化。 JDK开发人员就是那些专家。

    对您的具体问题:



    不幸的是没有,因为Random上的方法是synchronized。如果调用它们,ThreadLocalRandom将拉进Random的锁争用问题。为了从方法中删除synchronized关键字,TLR需要重写每个方法。



    首先,因为该字段仍然是私有(private)软件包,所以它实际上并不是在“违反封装”。它是由用户封装而成的,这是目标。在这里做出提高性能的决定时,我不会对此太挂了。有时性能是以正常的良好设计为代价的。实际上,这种“违规”是无法检测到的。该行为被简单地封装在两个类中,而不是单个类。

    将种子放入Thread中可以使ThreadLocalRandom完全无状态(除了initialized字段外,这在很大程度上是不相关的),因此整个应用程序只需要存在一个实例。



    许多JDK类都使用Unsafe。这是一种工具,不是罪过。再说一次,我只是不会对此感到太大压力。该类称为Unsafe,以阻止外行开发人员滥用它。我们相信/希望JDK作者足够聪明,可以知道何时可以安全使用。



    由于将永远只有一个ThreadLocalRandom实例,因此不需要将它作为实例变量。我想您可以选择是否也不需要static的情况,但那时候您只是在讨论样式。至少使static更清楚地使类基本上无状态。与文件中的mentioned一样,此字段不是必需的,但可以确保与Random相似的行为。

    关于java - 为什么ThreadLocalRandom如此奇怪地实现?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40620026/

    10-11 02:32