ThreadLocalRandom

1、Random

在建立Random类时会生成seed,seed用于生成随机数。在每次生成随机数后,都会使用CAS的方式更新seed,因此Random是线程安全的。可是在并发度较高的状况下,CAS的效率会变得很低。java

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

2、ThreadLocalRandom

  1. current()
static final ThreadLocalRandom instance = new ThreadLocalRandom();

public static ThreadLocalRandom current() {
    if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

current()首先查看当前线程中的PROBE(探针)是否初始化,若是是,则返回一个ThreadLocalRandom的单例;若是否,则调用localInit()进行初始化。算法

  1. localInit()
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);
}

localInit()生成probe和seed,并存入当前线程。probe和seed在Thread中的定义以下:安全

@sun.misc.Contended("tlr")
int threadLocalRandomProbe;

@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
  1. nextInt()
public int nextInt() {
    return mix32(nextSeed());
}

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是单例,全部线程共用一个,可是生成随机数的nextInt()倒是使用各自线程中的seed,线程之间是相互隔离的。因此ThreadLocalRandom在高并发场景下的性能要优于Random。并发

  1. 使用

错误使用:dom

public static void main(String[] args) throws Exception{
	ThreadLocalRandom threadLocalRandom=ThreadLocalRandom.current();
	for(int i=1; i<=10; i++){
		new Thread(()->{
			System.out.println(Thread.currentThread().getName()+":"+threadLocalRandom.nextInt());
		},String.valueOf(i)).start();
	}
}

结果:高并发

因为current()是在主线程中调用的,seed和probe初始化在了主线程中,而其余线程在调用nextInt时取到的seed和probe都为0,因为随机数生成算法都是固定的,因此也就生成了相同的随机数。性能

正确使用:this

public static void main(String[] args) throws Exception{
	for(int i=1; i<=10; i++){
		new Thread(()->{
			System.out.println(Thread.currentThread().getName()+":"+ThreadLocalRandom.current().nextInt());
		},String.valueOf(i)).start();
	}
}

结果:线程

必定要在各自的线程中初始化。code

相关文章
相关标签/搜索