brief introduction
In JDK7, Java util. Concurrent contains a fairly convenient class of random number generation, ThreadLocalRandom, when an application expects to use random numbers in multiple threads or ForkJoinTasks. For concurrent access, use TheadLocalRandom instead of math Random () can reduce competition for better performance. In use, you only need to call ThreadLocalRandom Current (), and then call one of its methods to get a random number. Here is an example:
int randomNum = ThreadLocalRandom.current().nextInt(max);
Source code analysis
linear congruential method
Linear congruent method is also called "linear congruent Random number generator". One of the methods to generate [0,1] uniformly distributed Random numbers. Including mixed congruence method and multiplicative congruence method. It was proposed by Lemmer in 1951. The Random number generation algorithm in Java is implemented through it.
X[n + 1] = (a * X[n] + c) mod m
Among them,
- M > 0, modulus data
- 0 < = a < = m, multiplier
- 0 < = C < = m, increment
- 0 < = X0 < m, X0 start value
Random source code analysis
The following is the core source code of Random
private static final long multiplier = 0x5DEECE66DL; private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1; // Constructor initializes this seed 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); } } protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; // CAS ensures thread safety, but there may be performance problems in multithreading } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); } // Get random number 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; }
From the source code, we can see that the core computing is
nextseed = (oldseed * multiplier + addend) & mask;
Then replace the fixed value to get the following formula
nextseed = (oldseed * 25214903917L + 11L) & 281474976710655L;
Seed in fact, we can also become a random seed and copy the formula again for easy reading
X[n + 1] = (a * X[n] + c) mod m
Where multiplier and append represent a and c in the formula respectively, but what does mask represent? Actually, x & [(1L < < 48) – 1] and x (mod 2^48) equivalent. Explain: x takes the remainder to the nth power of 2. Since the divisor is the nth power of 2, for example: 0001001000 is equivalent to moving the binary form of x to the right by N digits. At this time, the remainder is moved to the right of the decimal point, for example: 13 = 1101, 8 = 1000, 13 / 8 = 1.101. The 101 to the right of the decimal point is the remainder and converted into decimal is 5
Note: * * AtomicLong seed * * * * indicates that Random is a thread safe Random number tool class**
ThreadLocalRandom source code analysis
We can use threadlocalrandom Current() gets the instance.
public static ThreadLocalRandom current() { if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); return instance; }
If not initialized, localInit() will be called to initialize:
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); }
Through the code, we can see that thread Currentthread() gets the current thread. Note the process of generating random numbers no longer depends on CAS to obtain shared objects. Let's finally look at the nextInt method:
public int nextInt(int bound) { if (bound <= 0) throw new IllegalArgumentException(BadBound); // Generate random number int r = mix32(nextSeed()); int m = bound - 1; // Random number judgment if ((bound & m) == 0) // power of two // Remainder r &= m; else { // reject over-represented candidates // Retry until the interval requirements are met for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1) ; } return r; }
nextInt(int bound) has the same idea as nextInt, First call the mix32(nextSeed()) method to generate a random number (range of int type), and then judge the parameter n. if n is exactly the power of 2, the desired result can be obtained by direct shift; if it is not the power of 2, the remainder of n will be taken to make the result within the range of [0,n). In addition, the purpose of the for loop statement is to prevent the result from being negative.
Here we can see that it is mainly through mix32(nextSeed())
// Random number is generated according to the new seed. The random number algorithm is the same as random private static int mix32(long z) { z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32); } // Generate a new seed and save it in thread Threadlocalrandomseed. GAMMA=0x9e3779b97f4a7c15L 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; }
Use case
Random
The following is how the Random class generates Random numbers
public class RandomTest { static Random RANDOM = new Random(); public static void main(String[] args) { final int max = 1000000; for (int i = 0; i < 1000; i++) { new Thread(() -> { int randomNum = RANDOM.nextInt(max); System.out.println("randomNum: " + randomNum); }).start(); } } }
ThreadLocalRandom
Here is a simple ThreadLocalRandom, as follows:
public class ThreadLocalRandomTest { public static void main(String[] args) { final int max = 1000000; for (int i = 0; i < 1000; i++) { new Thread(()-> { int randomNum = ThreadLocalRandom.current().nextInt(max); System.out.println("randomNum: " + randomNum); }).start(); } } } // Output results randomNum: 648582 randomNum: 76984 randomNum: 561085 randomNum: 120131 randomNum: 210919 randomNum: 546645 randomNum: 194225 randomNum: 930034 randomNum: 203882
Usage Summary
Avoid the random instance being used by multiple threads. Although sharing the instance is thread safe, it will cause performance degradation due to competing for the same seed. Note: random instances include Java util. An instance of random or math Random(). Positive example: after JDK7, you can directly use API ThreadLocalRandom. Before JDK7, you need to code to ensure that each thread holds a separate random instance.
reference material
- The art of computer programming TACOP
- Java development manual
- https://www.cnblogs.com/shamo89/p/8052161.html
- http://ifeve.com/tag/threadlocalrandom
- https://www.cnblogs.com/softidea/p/5824240.html#3697214
- https://baike.baidu.com/item/%E7%BA%BF%E6%80%A7%E5%90%8C%E4%BD%99%E6%B3%95/10528746?fr=aladdin
- https://www.cnblogs.com/binarylei/p/10965556.html