JUC source code analysis - Other tools (a) ThreadLocalRandom

JUC source code analysis - Other tools (a) ThreadLocalRandom

ThreadLocalRandom is JDK7 new package at JUC random number generator that addresses Random seed only atomic variables within multiple threads multithreaded competition caused by insufficient number of threads spin retry. Note that Random itself is thread-safe. Meanwhile Random instances are not secure encryption, you can use java.security.SecureRandom to provide a reliable encryption.

1. Introduction random number algorithm

Random number algorithm commonly used in two ways: the congruence method (Congruential method) and Mersenne Twister (Mersenne twister). Random class is used in the remainder in the same method - the linear congruential method (see Donald Kunth "the art of computer programming," Volume II, chapter 3.2.1).

In the program so that the result of the expression is less than a certain value, the modulo operation is often used, the same result is a divisor of a remainder, this method is called congruential method (Congruential method).

Linear congruential method is a very old-random number generation algorithm, its mathematical form as follows:

Xn+1 = (a * Xn + c) % m

其中,m > 0, 0 < a < m, 0 < c < m

Look random number generation algorithm java source code - linear congruential algorithm

2. Random source code analysis

Random JDK classes of pseudo-random number is generated, using a 48-bit seed, and then calls the linear congruence equations, the code is very simple.

2.1 Data Structure

private static final long multiplier = 0x5DEECE66DL;    // 相当于上面表达式中的 a
private static final long mask = (1L << 48) - 1;        // 相当于上面表达式中的 m
private static final long addend = 0xBL;                // 相当于上面表达式中的 c

// seed 生成的随机数种子
private final AtomicLong seed;

2.2 Constructor

// ^ 让 seed 更加随机
public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}
public Random(long seed) {
    if (getClass() == Random.class)     
        // initialScramble 初始化的随机数
        this.seed = new AtomicLong(initialScramble(seed));
    else {
        this.seed = new AtomicLong();   // 子类重写 setSeed
        setSeed(seed);
    }
}

// 不太明白,不过也不影响代码阅读
private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
private static long seedUniquifier() {
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}

// 初始化的随机数
private static long initialScramble(long seed) {
    return (seed ^ multiplier) & mask;
}

The constructor initializes the random number seed seed, a random number is then calculated on this basis. If the incoming seed values, the then generated random number is the same.

@Test
public void test() {
    long seed = 343L;
    Random random1 = new Random(seed);
    Random random2 = new Random(seed);

    Assert.assertEquals(random1.nextInt(), random2.nextInt());
    Assert.assertEquals(random1.nextInt(), random2.nextInt());
    Assert.assertEquals(random1.nextInt(), random2.nextInt());
}

2.3 generates a random number

public int nextInt() {
    return next(32);
}
public int nextInt(int bound) {
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);
    // 1. 生成随机数
    int r = next(31);
    int m = bound - 1;
    // 2. 生成的随机数不能超过 bound。   (n&-n)==n 也可以判断2^n
    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 {
        oldseed = seed.get();
        // 就这么一句代码,对比上面的随机数算法
        nextseed = (oldseed * multiplier + addend) & mask;  
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}

Can be seen that the above code can be seen to generate a new random number requires two steps:

(1) first need to generate new seed based on the old seed.

(2) then calculates a new random number based on the new seed.

3. ThreadLocalRandom source code analysis

To address the high concurrency defects in multi-threaded under Random, the next JUC package adds ThreadLocalRandom class. Further references and contract in principle ThreadLocalRandom class analysis

3.1 ThreadLocalRandom principle

@Test
public void testThreadLocalRandom() {
    ThreadLocalRandom random = ThreadLocalRandom.current();
    System.out.println(random.nextInt());
}

From the name makes us think of the look appear ThreadLocal, ThreadLocal explain the Basics is to solve multiple threads access a variable time issue needs to be synchronized, so that each thread copy of variables, each thread of variables when in actual operation own local memory copy operation is inside, thus avoiding synchronized shared variables.

In fact ThreadLocalRandom realization of this principle is, the disadvantage is that multiple threads Random will use the same seed atomic variables, variables can lead to atomic updates competition. So if each thread maintains its own seeds of a variable, each thread to generate random numbers when computing a new seed according to their old seeds, and use the new seeds to update the old seed, a random number is then calculated according to the new seeds would not exist competition issues. This will greatly improve concurrency, as shown ThreadLocalRandom principle:

ThreadLocalRandom principle

3.2 Data Structure

ThreadLocalRandom类图

From the figure you can see ThreadLocalRandom class and a different storage Random seed, ThreadLocalRandom seed variables stored in the variable Thread.threadLocalRandomSeed by Unsafe operation of this variable. About threadLocalRandomSeed, threadLocalRandomProbe, threadLocalRandomSecondarySeed these three variables, Thread class has a related note:

/** The current seed for a ThreadLocalRandom */
// 1. 和 Random 中的 seed 类似
long threadLocalRandomSeed;

/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
// 2. 在 CurrentHashMap 中有使用。probe 是探测的意思,
int threadLocalRandomProbe;

/** Secondary seed isolated from public ThreadLocalRandom sequence */
int threadLocalRandomSecondarySeed;

Note that these three values ​​are not 0, because 0 has a special meaning in ThreadLocalRandom, the representation has not been initialized, calls localInit () to initialize.

Constructor 3.3

boolean initialized;
private ThreadLocalRandom() {
    initialized = true; // false during super() call
}
public static ThreadLocalRandom current() {
    if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

ThreadLocalRandom constructor is private and can only be constructed by current methods, or if PROBE default value of 0 means uninitialized, call localInit initialized.

3.4 generates a random number nextInt

// Random 一样也有两步:一是根据老的种子生成新的种子;
//                     二是根据新的种子来计算新的随机数
public int nextInt() {
    return mix32(nextSeed());
}

public int nextInt(int bound) {
    if (bound <= 0)
        throw new IllegalArgumentException(BadBound);
    int r = mix32(nextSeed());
    // 1. 生成随机数
    int m = bound - 1;
    // 2. 生成的随机数不能超过 bound
    // 2.1 bound 是 z^n 则直接取余
    if ((bound & m) == 0) // power of two
        r &= m;
    // 2.2 没看明白,但肯定是取 [0, bound] 之间的数
    else { // reject over-represented candidates
        for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1)
            ;
    }
    return r;
}

ThreadLocalRandom and Random as there are two steps:

(1) in accordance with the old seed to generate a new seed;

(2) to calculate a new random number based on the new seed.

nextInt (int bound) ideas and nextInt is the same, first call mix32 (nextSeed ()) function generates a random number (in the range of type int), and then the parameter n is determined, if n happens to be a power of 2, then directly shifting the desired result can be obtained; if not a power of 2, modulo n then on, eventually result in [0, n) range. In addition, for the purpose of loop should be to prevent the result is negative.

// 生成新的种子,保存在 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;
}
// 根据新种子生成随机数,随机数算法和 Random 一样了
private static int mix32(long z) {
    z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
    return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
}

3.5 Other methods

(1) getProbe

getProbe usage reference ConcurrentHashMap # fullAddCount method.

static final int getProbe() {
    return UNSAFE.getInt(Thread.currentThread(), PROBE);
}

static final int advanceProbe(int probe) {
    probe ^= probe << 13;   // 异或位运算。 xorshift
    probe ^= probe >>> 17;
    probe ^= probe << 5;
    UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
    return probe;
}

(2) nextSecondarySeed

    static final int nextSecondarySeed() {
    int r;
    Thread t = Thread.currentThread();
    if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
        r ^= r << 13;   // xorshift
        r ^= r >>> 17;
        r ^= r << 5;
    }
    else {
        localInit();
        if ((r = (int)UNSAFE.getLong(t, SEED)) == 0)
            r = 1; // avoid zero
    }
    UNSAFE.putInt(t, SECONDARY, r);
    return r;
}

reference:

  1. And contracting in principle ThreadLocalRandom class analysis
  2. "ThreadLocalRandom and Random Performance Test": http://www.importnew.com/12460.html
  3. "Java in the random function is how to achieve": https://my.oschina.net/hosee/blog/600392
  4. "Decrypted random number generator": https://blog.csdn.net/lihui126/article/details/46236109

The intentions of recording a little bit every day. Perhaps the content is not important, but the habit is very important!

Guess you like

Origin www.cnblogs.com/binarylei/p/10965556.html