1. The Random class and its limitations
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
// 计算新的种子
int r = next(31);
int m = bound - 1;
// 根据新的种子计算随机数
if ((bound & m) == 0) // i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
;
}
return r;
}
protected int next(int bits) {
long oldseed, nextseed;
// 这是一个原子性的变量
AtomicLong seed = this.seed;
do {
// (1)、获取老的种子
oldseed = seed.get();
// (2)、计算出新的种子
nextseed = (oldseed * multiplier + addend) & mask;
// (3)、CAS操作更新老的种子
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
Random summary:
Interview : What are the problems with Random under multithreading?
Each Random instance has a atomic to record the current seed value. When a new random number is to be generated, a new seed needs to be calculated based on the current seed and the seed variable is updated. When in a multi-threaded environment, multiple threads will compete for the update operation of the same atomic variable. Since the CAS operation is performed during the update of the atomic variable, only one thread will succeed at the same time, which will cause a large number of threads to spin retries, thereby reducing concurrency performance.
Possible symptoms:
If there are a lot of concurrent requests and the spin lock keeps retrying, the CPU will continue to soar.
2、ThreadLocalRandom
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);
}
This method is used to create a ThreadLocalRandom random number generator. If the variable value of threadLocalRandomProbe in the current thread is 0, it means that the current method is called for the first time, and then the localInit method is called to initialize the seed variable.
Lazy initialization is used here. In the localInit method, the seed variable is not initialized, but the seed variable is generated when a random number needs to be generated. This is an optimization.
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
// 生成新种子(获取当前线程种子 + 种子增量)
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
mix32 is a fixed algorithm. Here we will focus on the nextSeed method, which is initialized when it is called for the first time, to get the value of threadLocalRandomSeed of the current thread (the default value is 0 for the first time) + seed increment, if it is not the first time Get the value of the old seed + seed increment to generate a new seed variable and set it back. In this way, competition is avoided in a multi-threaded environment, because threadLocalRandomSeed is a variable of Thread, which belongs to the thread level.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。