随机数产生:1.产生新的seed,2.根据seed计算出新的随机数
Random的用法:
Random random = new Random();
random.nextInt(100);
public int nextInt(int n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do {
// next方法根据seed生成随机数
bits = next(31);
val = bits % n;
} while (bits - val + (n-1) < 0);
return val;
}
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
// CAS生成seed,多线程环境下会存在竞争
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
ThreadLocalRandom优化了Random在多线程下竞争seed,高版本的dubbo在随机负载中就把Random改为了ThreadLocalRandom
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
int random = threadLocalRandom.nextInt(100);
- 1.7代码
current()方法:
public static ThreadLocalRandom current() {
return localRandom.get();
}
/**
* The actual ThreadLocal
*/
private static final ThreadLocal<ThreadLocalRandom> localRandom =
new ThreadLocal<ThreadLocalRandom>() {
protected ThreadLocalRandom initialValue() {
return new ThreadLocalRandom();
}
};
调用current()方法每次都会拿到当前线程对应的ThreadLocalRandom(),初始化根据nanotime生成了新的seed
ThreadLocalRandom() {
super();
initialized = true;
}
// 父类初始化seed属性
public Random() {
// 这里根据时间计算出seed,并设置了seed值
this(seedUniquifier() ^ System.nanoTime());
}
// 回调ThreadLocalRandom的设置seed方法,
// ThreadLocalRandom中seed是rnd来做的
public void setSeed(long seed) {
if (initialized)
throw new UnsupportedOperationException();
rnd = (seed ^ multiplier) & mask;
}
最后调用nextInt方法,父类回调next,根据rnd值生成随机数,并重新设置了rnd的值,保证下次线程进来会拿新的rnd计算
protected int next(int bits) {
rnd = (rnd * multiplier + addend) & mask;
return (int) (rnd >>> (48-bits));
}
因为使用了ThreadLocal,假如没有使用线程池,这个会变的很大,而且方法里没有维护删除,会存在一直变大的问题,不可控。
所以1.8有了改进
- 1.8代码
1.8是通过Unsafe方法实现的。
ThreadLocalRandom初始化的时候,初始化了一些属性。Seed拿到threadLocalRandomSeed属性的偏移量
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);
}
}
current()方法:
public static ThreadLocalRandom current() {
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);
}
nextInt()方法:
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;
}
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
// 1.拿到当前线程
// 2.根据偏移量拿到新的内存值(一大串数字)并返回
// 3.新的内存值设置到线程偏移量上面
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
总结:
1.7:seed设置到了每个threadlocal对应的ThreadLocalRandom实例上面,进行随机数计算
1.8:seed是根据unsafe方法设置的内存地址来计算随机数
备注:
1.7中private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
1.8获取属性:java.lang.Thread#threadLocalRandomSeed
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
涉及到伪共享问题