ThreadLocalRandom

编程

一、Random

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

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

  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. 使用

错误使用:

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,由于随机数生成算法都是固定的,所以也就生成了相同的随机数。

正确使用:

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

}

}

结果:

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

以上是 ThreadLocalRandom 的全部内容, 来源链接: utcz.com/z/514574.html

回到顶部