ThreadLocalRandom
线程局部随机数
高并发下的ThreadLocalRandom和Random
public class RandomTest {
public static void main(String[] args) throws InterruptedException {
Random random=new Random();
System.out.println("###################Random###################");
for (int i=1;i<8;i++){
concurrentGetRandom(8, (int) Math.pow(10,i),random);
}
ThreadLocalRandom threadLocalRandom=ThreadLocalRandom.current();
System.out.println("###############ThreadLocalRandom##############");
for (int i=1;i<8;i++){
concurrentGetRandom(8, (int) Math.pow(10,i),threadLocalRandom);
}
}
private static void concurrentGetRandom(int threadSize,int randomNum,Random random) throws InterruptedException {
long start = System.currentTimeMillis();
CountDownLatch countDownLatch=new CountDownLatch(threadSize);
for (int i=0;i<threadSize;i++){
new Thread(()->{
for (int j=0;j<randomNum;j++){
random.nextInt();
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
long end= System.currentTimeMillis();
System.out.println("threadSize: "+threadSize+" randomNum: "+randomNum+" cost "+(end-start)/1000.0+"s");
}
}
从结果可以看出当固定线程数,逐渐增加调用生成随机数的次数(也就是说竞争更激烈),Random消耗的时间几乎指数级别增长,而ThreadLocalRandom消耗的时间几乎线性增长
###################Random###################
threadSize: 8 randomNum: 10 cost 0.051s //这里可能是没有生成热点代码
threadSize: 8 randomNum: 100 cost 0.001s
threadSize: 8 randomNum: 1000 cost 0.002s
threadSize: 8 randomNum: 10000 cost 0.006s
threadSize: 8 randomNum: 100000 cost 0.047s
threadSize: 8 randomNum: 1000000 cost 0.424s
threadSize: 8 randomNum: 10000000 cost 5.583s
###############ThreadLocalRandom##############
threadSize: 8 randomNum: 10 cost 0.001s
threadSize: 8 randomNum: 100 cost 0.001s
threadSize: 8 randomNum: 1000 cost 0.002s
threadSize: 8 randomNum: 10000 cost 0.004s
threadSize: 8 randomNum: 100000 cost 0.001s
threadSize: 8 randomNum: 1000000 cost 0.005s
threadSize: 8 randomNum: 10000000 cost 0.042s
原理
Random
使用Random多个线程会竞争同一个原子变量种子
ThreadLocalRandom
使用ThreadLocalRandom多个线程各自使用线程独有的种子
Random
部分属性
//使用AtomicLong作为种子来保证种子操作的原子性
private final AtomicLong seed;
//用来生成新种子的一些数
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
//value在AtomicLong对象中的偏移量,用于修改seed
private static final long valueOffset;
构造方法
/**
* Creates a new random number generator. This constructor sets
* the seed of the random number generator to a value very likely
* to be distinct from any other invocation of this constructor.
* 默认利用种子唯一标识符异或当前纳秒时间作为种子构造Random实例
*/
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
//种子唯一标识符生成器
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
//种子唯一标识符
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L);
/**
* Creates a new random number generator using a single {@code long} seed.
* The seed is the initial value of the internal state of the pseudorandom
* number generator which is maintained by method {@link #next}.
*
* <p>The invocation {@code new Random(seed)} is equivalent to:
* <pre> {@code
* Random rnd = new Random();
* rnd.setSeed(seed);}</pre>
*
* @param seed the initial seed
* @see #setSeed(long)
*/
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
生成随机数方法
nextInt
public int nextInt() {
return next(32);
}
next
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
//使用CAS来修改种子
do {
oldseed = seed.get();
//某种算法,不懂
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
可以看出Random类共用一个种子,并且是使用CAS来更新种子,在高并发场景中CAS会导致大量的忙等待(CPU)占用,而且效率低下
Thread
相关属性
//都使用了字节填充
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
ThreadLocalRandom
类图
属性
//借助于Unsafe使用CAS机制来修改线程中的种子变量
// Unsafe mechanics
private 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);
}
}
构造方法
/** Constructor used only for static singleton */
//饿汉式单例模式
private ThreadLocalRandom() {
initialized = true; // false during super() call
}
/** The common ThreadLocalRandom */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
//获取实例
public static ThreadLocalRandom current() {
//如果线程探针为0那就对其初始化
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
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);
}
生成随机数方法
public int nextInt(int origin, int bound) {
if (origin >= bound)//边界校验
throw new IllegalArgumentException(BadRange);
return internalNextInt(origin, bound);
}
//获取随机数
final int internalNextInt(int origin, int bound) {
int r = mix32(nextSeed());
if (origin < bound) {
int n = bound - origin, m = n - 1;
if ((n & m) == 0)
r = (r & m) + origin;
else if (n > 0) {
for (int u = r >>> 1;
u + m - (r = u % n) < 0;
u = mix32(nextSeed()) >>> 1)
;
r += origin;
}
else {
while (r < origin || r >= bound)
r = mix32(nextSeed());
}
}
return r;
}
//更新并返回种子
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;
}