1.简介
A random number generator isolated to the current thread. Like
the global [`Random`](https://docs.oracle.com/javase/8/docs/api/java/util/Random.html
"class in java.util") generator used by the [`Math`]
(https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html
"class in java.lang") class, a `ThreadLocalRandom` is initialized
with an internally generated seed that may not otherwise be
modified. When applicable, use of `ThreadLocalRandom` rather
than shared `Random` objects in concurrent programs will typically
encounter much less overhead and contention. Use
of `ThreadLocalRandom` is particularly appropriate when multiple
tasks (for example, each a [`ForkJoinTask`]
(https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/For
kJoinTask.html "class in java.util.concurrent")) use random
numbers in parallel in thread pools.
隔离到当前线程的随机数生成器。与Math使用全局Random生成器一样,ThreadLocalRandom使用内部生成的种子进行初始化,否则可能无法修改。 在并发程序中使用ThreadLocalRandom而不是共享Random会减少开销和竞争。当线程池中多个任务并行使用随机数时,ThreadLocalRandom特别合适。
Usages of this class should typically be of the
form: `ThreadLocalRandom.current().nextX(...)` (where `X` is `Int`,
`Long`, etc). When all usages are of this form, it is never possible
to accidently share a `ThreadLocalRandom` across multiple
threads.
形式:ThreadLocalRandom.current().nextX(),当所有用法都是这种形式时,永远不可能再多个线程中意外地共享ThreadLocalRandom
This class also provides additional commonly used bounded
random generation methods.
还提供了其他常用的有界随机生成方法。
Instances of `ThreadLocalRandom` are not cryptographically
secure. Consider instead using [`SecureRandom`]
(https://docs.oracle.com/javase/8/docs/api/java/security/SecureRa
ndom.html "class in java.security") in security-sensitive
applications. Additionally, default-constructed instances do not use
a cryptographically random seed unless the [system property]
(https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#
getProperty-java.lang.String-) `java.util.secureRandomSeed` is set
to `true`.
加密安全性请使用SecureRandom。
2.实现简介
This class implements the java.util.Random API (and subclasses
Random) using a single static instance that accesses random
number state held in class Thread (primarily, field
threadLocalRandomSeed). In doing so, it also provides a home
for managing package-private utilities that rely on exactly the
same state as needed to maintain the ThreadLocalRandom
instances. We leverage the need for an initialization flag
field to also use it as a "probe" -- a self-adjusting thread
hash used for contention avoidance, as well as a secondary
simpler (xorShift) random seed that is conservatively used to
avoid otherwise surprising users by hijacking the
ThreadLocalRandom sequence. The dual use is a marriage of
convenience, but is a simple and efficient way of reducing
application-level overhead and footprint of most concurrent
programs. Even more opportunistically, we also define here
other package-private utilities that access Thread class
fields.
此类用单个静态实例实现RandomAPI,该实例访问Thread中的随机数状态(主要是threadLocalRandomSeed)。
probe ——自调整线程哈希,用于避免争用。
xorShift ——随机种子
Even though this class subclasses java.util.Random, it uses the
same basic algorithm as java.util.SplittableRandom. (See its
internal documentation for explanations, which are not repeated
here.) Because ThreadLocalRandoms are not splittable
though, we use only a single 64bit gamma.
使用了与SplittableRandom相同的基本算法。
Because this class is in a different package than class Thread,
field access methods use Unsafe to bypass access control rules.
To conform to the requirements of the Random superclass
constructor, the common static ThreadLocalRandom maintains an
"initialized" field for the sake of rejecting user calls to
setSeed while still allowing a call from constructor. Note
that serialization is completely unnecessary because there is
only a static singleton. But we generate a serial form
containing "rnd" and "initialized" fields to ensure
compatibility across versions.
此类与Thread所在的包不同,所以需要使用Unsafe绕开访问控制规则。
Implementations of non-core methods are mostly the same as in
SplittableRandom, that were in part derived from a previous
version of this class.
The nextLocalGaussian ThreadLocal supports the very rarely used
nextGaussian method by providing a holder for the second of a
pair of them. As is true for the base class version of this
method, this time/space tradeoff is probably never worthwhile,
but we provide identical statistical properties.
3.方法
/**
* Returns the current thread's {@code ThreadLocalRandom}.
*
* @return the current thread's {@code ThreadLocalRandom}
*/
public static ThreadLocalRandom current() {
if (U.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
/**
* Initialize Thread fields for the current thread. Called only
* when Thread.threadLocalRandomProbe is zero, indicating that a
* thread local seed value needs to be generated. Note that even
* though the initialization is purely thread-local, we need to
* rely on (static) atomic generators to initialize the values.
*/
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();
U.putLong(t, SEED, seed);
U.putInt(t, PROBE, probe);
}
/** Generates per-thread initialization/probe field */
private static final AtomicInteger probeGenerator = new AtomicInteger();
/** The common ThreadLocalRandom */
static final ThreadLocalRandom instance = new ThreadLocalRandom();
/** The current seed for a ThreadLocalRandom */
@jdk.internal.vm.annotation.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomSecondarySeed;
每个线程都都持有一个本地的种子变量,这样在计算随机数时就不会存在竞争问题了。
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;
}
从上面源码可以看出:所有线程共享一个静态的Random实例instance,但是各线程的种子并不相同,存储在线程的属性threadLocalRandomSeed中。所以各线程产生的随机数序列均不相同。
4.实例
public class CsGameByThreadLocal {
private static final Integer BULLET_NUMBER = 1500;
private static final Integer KILLED_ENEMIES = 0;
private static final Integer LIFE_VALUE = 10;
private static final Integer TOTAL_PLAYERS = 10;
// private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
private static final ThreadLocal BULLET_NUMBER_THREADLOCAL
= new ThreadLocal() {
@Override
protected Integer initialValue() {
return BULLET_NUMBER;
}
};
private static final ThreadLocal KILLED_ENEMIES_THREADLOCAL
= new ThreadLocal() {
@Override
protected Integer initialValue() {
return KILLED_ENEMIES;
}
};
private static final ThreadLocal LIFE_VALUE_THREADLOCAL
= new ThreadLocal() {
@Override
protected Integer initialValue() {
return LIFE_VALUE;
}
};
private static class Player extends Thread {
@Override
public void run() {
ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
Integer bullets = BULLET_NUMBER_THREADLOCAL.get() - RANDOM.nextInt(BULLET_NUMBER);
Integer killEnemies = KILLED_ENEMIES_THREADLOCAL.get() + RANDOM.nextInt(TOTAL_PLAYERS / 2);
Integer lifeValue = LIFE_VALUE_THREADLOCAL.get() - RANDOM.nextInt(LIFE_VALUE);
System.out.println(getName() + ", BULLET_NUMBER is " + bullets);
System.out.println(getName() + ", KILLED_ENEMIES is " + killEnemies);
System.out.println(getName() + ", LIFE_VALUE is " + lifeValue);
BULLET_NUMBER_THREADLOCAL.remove();
KILLED_ENEMIES_THREADLOCAL.remove();
LIFE_VALUE_THREADLOCAL.remove();
}
}
public static void main(String[] args) {
for (int i = 0; i < TOTAL_PLAYERS; i++) {
new Player().start();
}
}
}
从上图可以看出各线程共享同一个RANDOM实例:ThreadLocalRandom@539.