目录
一:简述
二:Random的性能差在哪里
三:ThreadLocalRandom的简略应用
四:为什么ThreadLocalRandom能在保障线程平安的状况下还能有不错的性能

一:简述
如果咱们想要生成一个随机数,通常会应用Random类。然而在并发状况下Random生成随机数的性能并不是很现实,明天给大家介绍一下JUC包中的用于生成随机数的类--ThreadLocalRandom.(本文基于JDK1.8)

二:Random的性能差在哪里
Random随机数生成是和种子seed无关,而为了保障线程安全性,Random通过CAS机制来保障线程安全性。从next()办法中咱们能够发现seed是通过自旋锁和CAS来进行批改值的。如果在高并发的场景下,那么可能会导致CAS一直失败,从而导致一直自旋,这样就可能会导致服务器CPU过高。

protected int next(int bits) {        long oldseed, nextseed;        AtomicLong seed = this.seed;        do {            oldseed = seed.get();            nextseed = (oldseed * multiplier + addend) & mask;        } while (!seed.compareAndSet(oldseed, nextseed));        return (int)(nextseed >>> (48 - bits));    }

三:ThreadLocalRandom的简略应用
应用的办法很简略,通过ThreadLocalRandom.current()获取到ThreadLocalRandom实例,而后通过nextInt(),nextLong()等办法获取一个随机数。

代码:

@Testvoid test() throws InterruptedException {    new Thread(()->{        ThreadLocalRandom random = ThreadLocalRandom.current();        System.out.println(random.nextInt(100));    }).start();    new Thread(()->{        ThreadLocalRandom random = ThreadLocalRandom.current();        System.out.println(random.nextInt(100));    }).start();     Thread.sleep(100);}

运行后果:

四:为什么ThreadLocalRandom能在保障线程平安的状况下还能有不错的性能
咱们能够看一下ThreadLocalRandom的代码实现。

首先咱们很容易看出这是一个饿汉式的单例

/** Constructor used only for static singleton */private ThreadLocalRandom() {    initialized = true; // false during super() call} /** The common ThreadLocalRandom */static final ThreadLocalRandom instance = new ThreadLocalRandom();

咱们能够看到PROBE成员变量代表的是Thread类的threadLocalRandomProbe属性的内存偏移量,SEED成员变量代表的是Thread类的threadLocalRandomSeed属性的内存偏移量,SECONDARY成员变量代表的是Thread类的threadLocalRandomSecondarySeed属性的内存偏移量。

// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long SEED;private static final long PROBE;private static final long SECONDARY;static {    try {        UNSAFE = sun.misc.Unsafe.getUnsafe();        Class<?> tk = Thread.class;        SEED = UNSAFE.objectFieldOffset            (tk.getDeclaredField("threadLocalRandomSeed"));        PROBE = UNSAFE.objectFieldOffset            (tk.getDeclaredField("threadLocalRandomProbe"));        SECONDARY = UNSAFE.objectFieldOffset            (tk.getDeclaredField("threadLocalRandomSecondarySeed"));    } catch (Exception e) {        throw new Error(e);    }}

能够看到Thread类中的确有这三个属性

Thread类:

@sun.misc.Contended("tlr")//以后Thread的随机种子 默认值是0long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */@sun.misc.Contended("tlr")//用来标记以后Thread的threadLocalRandomSeed是否进行了初始化 0代表没有,非0代表曾经初始化 默认值是0int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */@sun.misc.Contended("tlr")//以后Thread的二级随机种子 默认值是0int threadLocalRandomSecondarySeed;

接下来咱们看ThreadLocalRandom.current()办法。

ThreadLocalRandom.current()

ThreadLocalRandom.current()的作用次要是初始化随机种子,并且返回ThreadLocalRandom的实例。

首先通过UNSAFE类获取以后线程的Thread对象的threadLocalRandomProbe属性,看随机种子是否曾经初始化。没有初始化,那么调用localInit()办法进行初始化

public static ThreadLocalRandom current() {        // 获取以后线程的        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)            localInit();        return instance;    }

localInit()

localInit()办法的作用就是初始化随机种子,能够看到代码很简略,就是通过UNSAFE类对以后Thread的threadLocalRandomProbe属性和threadLocalRandomSeed属性进行一个赋值。

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);    }

接下来以nextInt()办法为例,看ThreadLocalRandom是如何生成到随机数的。咱们能够看出随机数正是通过nextSeed()办法获取到随机种子,而后通过随机种子而生成。所以重点看nextSeed()办法是如何获取到随机种子的。

public int nextInt(int bound) {        if (bound <= 0)            throw new IllegalArgumentException(BadBound);        int r = mix32(nextSeed());        int m = bound - 1;        if ((bound & m) == 0) // power of two            r &= m;        else { // reject over-represented candidates            for (int u = r >>> 1;                 u + m - (r = u % bound) < 0;                 u = mix32(nextSeed()) >>> 1)                ;        }        return r;    }

nextSeed()

nextSeed()办法的作用是获取随机种子,代码很简略,就是通过UNSAFE类获取以后线程的threadLocalRandomSeed属性,并且将原来的threadLocalRandomSeed加上GAMMA设置成新的threadLocalRandomSeed。

final long nextSeed() {        Thread t; long r; // read and update per-thread seed        UNSAFE.putLong(t = Thread.currentThread(), SEED,                       r = UNSAFE.getLong(t, SEED) + GAMMA);        return r;    }

小结:

ThreadLocalRandom为什么线程平安?是因为它将随机种子保留在以后Thread对象的threadLocalRandomSeed变量中,这样每个线程都有本人的随机种子,实现了线程级别的隔离,所以ThreadLocalRandom也并不需要像Random通过自旋锁和cas来保障随机种子的线程安全性。在高并发的场景下,效率也会绝对较高。

注:各位有没有发现ThreadLocalRandom保障线程平安的形式和ThreadLocal有点像呢

须要留神的点:

1.ThreadLocalRandom是单例的。

2.咱们每个线程在获取随机数之前都须要调用一下ThreadLocalRandom.current()来初始化以后线程的随机种子。

3.了解ThreadLocalRandom须要对UnSafe类有所理解,它是Java提供的一个能够间接通过内存对变量进行获取和批改的一个工具类。java的CAS也是通过这个工具类来实现的。