JUCソースコード解析 - その他のツール(a)のThreadLocalRandom

JUCソースコード解析 - その他のツール(a)のThreadLocalRandom

ThreadLocalRandomはアドレスJUC乱数生成器でJDK7新しいパッケージである複数のスレッド内でのみアトミック変数は、スレッドの数が不十分に起因する競争をマルチスレッドランダムシードを再試行してくださいスピン。ランダム自体はスレッドセーフであることに注意してください。一方、ランダムなインスタンスを使用して、信頼性の高い暗号化を提供するためにjava.security.SecureRandomを使用することができ、安全な暗号化ではありません。

1.はじめに乱数アルゴリズム

一般的に二つの方法で使用される乱数アルゴリズム:合同法(合同法)、メルセンヌツイスター(メルセンヌツイスター)。線形合同法(章3.2.1巻II「コンピュータプログラミングの技術」ドナルドKunthを参照) - ランダムクラスは、同じ方法で残りの部分で使用されています。

プログラム中での発現の結果が一定値未満になるように、モジュロ演算が用いられることが多い、同一の結果が残りの除数であり、この方法は、合同法(合同法)と呼ばれています。

次のように線形合同法は、非常に古い乱数生成アルゴリズム、その数学的な形式であります:

X N + 1 =(*はX N + C)%mを

其中、M> 0、0 <a <M、0 <C <M

線形合同アルゴリズム - 乱数生成アルゴリズムJavaのソースコードを見て

2.ランダムなソースコード解析

擬似乱数のランダムJDKクラス48ビットのシードを使用して、生成され、その後、線形合同式を呼び出し、コードは非常に簡単です。

2.1データ構造

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コンストラクタ

// ^ 让 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;
}

コンストラクタは、乱数シードの種子、乱数は、次いで、これに基づいて算出される初期化します。着信シード値場合、生成された乱数と同じです。

@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は、乱数を生成し、

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));
}

上記のコードは、新たな乱数を生成するために見ることができることがわかる2つの手順が必要です。

(1)まず古いシードに基づいて新しいシードを生成する必要があります。

(2)新しいシードに基づいて新たな乱数を算出します。

3. ThreadLocalRandomソースコード解析

ランダム下のマルチスレッドでの並行性の高い欠陥に対処するために、次のJUCパッケージはThreadLocalRandomクラスを追加します。原則ThreadLocalRandomクラス分析におけるさらなる参照と契約

3.1 ThreadLocalRandom原理

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

名前から、私たちは見ての考えさせるのThreadLocal、ThreadLocalの基本を説明表示される複数のスレッドを解決することであり、変数の各スレッドコピーするように、変数の各スレッドは時に実際の動作中に可変時間問題は、同期させる必要があるアクセス自身のローカルメモリコピー操作は、このように、同期共有変数を避け、内部のです。

実際にはThreadLocalRandomこの原則の実現は、ある欠点は、複数のスレッドランダム変数はアトミックな更新の競争につながることができます同じシードアトミック変数を、使用することです。各スレッドは、変数の独自の種を維持してあれば、各スレッド新しいシードを計算するときには、古い種に応じて乱数を生成し、古いシード、乱数は、その後、新しい種に応じて算出されて更新する新しい種を使用するためには存在しなかったでしょう競争の問題。これは大幅に示したThreadLocalRandom原則として、同時実行性を向上します。

ThreadLocalRandom原理

3.2データ構造

ThreadLocalRandom类图

図から、あなたはThreadLocalRandomクラスと異なるストレージランダムシード、この変数の危険な操作でThread.threadLocalRandomSeed変数に格納されThreadLocalRandomシード変数を見ることができます。threadLocalRandomSeed、threadLocalRandomProbeについては、これら3つの変数をthreadLocalRandomSecondarySeed、Threadクラスは、関連のメモを持っています:

/** 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;

これらの三つの値が0でないことに注意してください、0はThreadLocalRandomで特別な意味を持っているので、表現が初期化されていない、初期化するためにlocalInit()を呼び出します。

コンストラクタ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コンストラクタがプライベートであり、唯一の現在の方法によって構築することができ、または0のPROBEのデフォルト値が初期化されていない場合を意味し、localInit初期化を呼び出します。

3.4は、乱数を生成し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;
}

2つのステップがあるとしてThreadLocalRandomとランダム:

(1)旧シードに従って新しいシードを生成します。

(2)新たなシードに基づいて新たな乱数を計算します。

nは、直接、2の累乗であることを起こる場合nextInt(INTバウンド)アイデアやnextInt同じで、最初のコールmix32(nextSeed())関数は、(int型の範囲内で)乱数を生成し、パラメータnが決定されます所望の結果をシフトして得ることができ、その後に2の累乗モジュロNではない場合、最終的には[0、n)の範囲をもたらします。また、ループの目的のために結果が否定的である防ぐためであるべきです。

// 生成新的种子,保存在 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その他の方法

(1)getProbe

getProbe使用量参照のConcurrentHashMap#fullAddCount方法。

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;
}

参考:

  1. そして、原則ThreadLocalRandomクラス分析で契約
  2. 「ThreadLocalRandomとランダム性能試験」:http://www.importnew.com/12460.html
  3. 「ランダム機能でJavaが実現する方法である」:https://my.oschina.net/hosee/blog/600392
  4. 「復号化された乱数ジェネレータ」:https://blog.csdn.net/lihui126/article/details/46236109

毎日少しを記録する意向。おそらく、内容は重要ではありませんが、習慣は非常に重要です!

おすすめ

転載: www.cnblogs.com/binarylei/p/10965556.html