ThreadLocalRandom

java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。

安全随机生成器

  • 需要生成加密性强的随机数据的时候才用它
  • 生成速度慢

如果需要生成大量的随机数据,可能会产生阻塞需要等待外部中断事件
而伪随机生成器,只依赖于“seed”的初始值,如果给生成算法提供相同的seed,可以得到一样的伪随机序列。一般情况下,由于它是计算密集型的(不依赖于任何IO设备),因此生成速度更快。以下是伪随机生成器的进化史。

java.util.Random

自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。
Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕,后面的测试结果中可以看到它的糟糕表现。

java.util.concurrent.ThreadLocalRandom

1.7增加该类,企图将它和Random结合以克服所有的性能问题,该类继承自Random。
ThreadLocalRandom的主要实现细节:

  • 使用一个普通的long而不是使用Random中的AtomicLong作为seed
  • 不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()
  • 它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行

从Math.random()改变到ThreadLocalRandom有如下好处:

  • 我们不再有从多个线程访问同一个随机数生成器实例的争夺。
  • 取代以前每个随机变量实例化一个随机数生成器实例,我们可以每个线程实例化一个。
private static final AtomicInteger probeGenerator =
        new AtomicInteger();
  private static final AtomicLong seeder = new AtomicLong(initialSeed());
static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
    }

猜你喜欢

转载自blog.csdn.net/ljz2016/article/details/83862240