ThreadLocalRandom在1.7和1.8的区别

随机数产生:1.产生新的seed,2.根据seed计算出新的随机数

Random的用法:

Random random = new Random();
random.nextInt(100);
     public int nextInt(int n) {
        if (n <= 0)
            throw new IllegalArgumentException("n must be positive");

        if ((n & -n) == n)  // i.e., n is a power of 2
            return (int)((n * (long)next(31)) >> 31);

        int bits, val;
        do {
        	// next方法根据seed生成随机数
            bits = next(31);
            val = bits % n;
        } while (bits - val + (n-1) < 0);
        return val;
    }
    
	protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
            // CAS生成seed,多线程环境下会存在竞争
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

ThreadLocalRandom优化了Random在多线程下竞争seed,高版本的dubbo在随机负载中就把Random改为了ThreadLocalRandom

 ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
 int random = threadLocalRandom.nextInt(100);

- 1.7代码

current()方法:

    public static ThreadLocalRandom current() {
        return localRandom.get();
    }
        /**
     * The actual ThreadLocal
     */
    private static final ThreadLocal<ThreadLocalRandom> localRandom =
        new ThreadLocal<ThreadLocalRandom>() {
            protected ThreadLocalRandom initialValue() {
                return new ThreadLocalRandom();
            }
    };

调用current()方法每次都会拿到当前线程对应的ThreadLocalRandom(),初始化根据nanotime生成了新的seed

    ThreadLocalRandom() {
        super();
        initialized = true;
    }
    // 父类初始化seed属性
    public Random() {
    	// 这里根据时间计算出seed,并设置了seed值
        this(seedUniquifier() ^ System.nanoTime());
    }

	// 回调ThreadLocalRandom的设置seed方法,
	// ThreadLocalRandom中seed是rnd来做的
    public void setSeed(long seed) {
        if (initialized)
            throw new UnsupportedOperationException();
        rnd = (seed ^ multiplier) & mask;
    }
    

最后调用nextInt方法,父类回调next,根据rnd值生成随机数,并重新设置了rnd的值,保证下次线程进来会拿新的rnd计算

    protected int next(int bits) {
        rnd = (rnd * multiplier + addend) & mask;
        return (int) (rnd >>> (48-bits));
    }

因为使用了ThreadLocal,假如没有使用线程池,这个会变的很大,而且方法里没有维护删除,会存在一直变大的问题,不可控。
所以1.8有了改进

- 1.8代码
1.8是通过Unsafe方法实现的。
ThreadLocalRandom初始化的时候,初始化了一些属性。Seed拿到threadLocalRandomSeed属性的偏移量

    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

current()方法:

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

nextInt()方法:

    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
        // 1.拿到当前线程
        // 2.根据偏移量拿到新的内存值(一大串数字)并返回
        // 3.新的内存值设置到线程偏移量上面
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
    }

总结:
1.7:seed设置到了每个threadlocal对应的ThreadLocalRandom实例上面,进行随机数计算
1.8:seed是根据unsafe方法设置的内存地址来计算随机数

备注:
1.7中private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
1.8获取属性:java.lang.Thread#threadLocalRandomSeed

    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

涉及到伪共享问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值