ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

ThreadLocalRandom源码解析

2020-06-21 12:03:43  阅读:301  来源: 互联网

标签:ThreadLocalRandom int Random long seed static 解析 源码


ThreadLocalRandom

线程局部随机数

高并发下的ThreadLocalRandom和Random

public class RandomTest {
    public static void main(String[] args) throws InterruptedException {
        Random random=new Random();
        System.out.println("###################Random###################");
        for (int i=1;i<8;i++){
            concurrentGetRandom(8, (int) Math.pow(10,i),random);
        }
        ThreadLocalRandom threadLocalRandom=ThreadLocalRandom.current();
        System.out.println("###############ThreadLocalRandom##############");
        for (int i=1;i<8;i++){
            concurrentGetRandom(8, (int) Math.pow(10,i),threadLocalRandom);
        }
    }

    private static void concurrentGetRandom(int threadSize,int randomNum,Random random) throws InterruptedException {
        long start = System.currentTimeMillis();
        CountDownLatch countDownLatch=new CountDownLatch(threadSize);
        for (int i=0;i<threadSize;i++){
            new Thread(()->{
                for (int j=0;j<randomNum;j++){
                    random.nextInt();
                }
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();

        long end= System.currentTimeMillis();
        System.out.println("threadSize: "+threadSize+" randomNum: "+randomNum+" cost "+(end-start)/1000.0+"s");
    }
}

从结果可以看出当固定线程数,逐渐增加调用生成随机数的次数(也就是说竞争更激烈),Random消耗的时间几乎指数级别增长,而ThreadLocalRandom消耗的时间几乎线性增长

###################Random###################
threadSize: 8 randomNum: 10 cost 0.051s //这里可能是没有生成热点代码
threadSize: 8 randomNum: 100 cost 0.001s
threadSize: 8 randomNum: 1000 cost 0.002s
threadSize: 8 randomNum: 10000 cost 0.006s
threadSize: 8 randomNum: 100000 cost 0.047s
threadSize: 8 randomNum: 1000000 cost 0.424s
threadSize: 8 randomNum: 10000000 cost 5.583s
###############ThreadLocalRandom##############
threadSize: 8 randomNum: 10 cost 0.001s
threadSize: 8 randomNum: 100 cost 0.001s
threadSize: 8 randomNum: 1000 cost 0.002s
threadSize: 8 randomNum: 10000 cost 0.004s
threadSize: 8 randomNum: 100000 cost 0.001s
threadSize: 8 randomNum: 1000000 cost 0.005s
threadSize: 8 randomNum: 10000000 cost 0.042s

原理

Random

使用Random多个线程会竞争同一个原子变量种子

avatar

ThreadLocalRandom

使用ThreadLocalRandom多个线程各自使用线程独有的种子

avatar

Random

部分属性

    //使用AtomicLong作为种子来保证种子操作的原子性
	private final AtomicLong seed;

	//用来生成新种子的一些数
    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L << 48) - 1;

    // setup to use Unsafe.compareAndSwapLong for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
	//value在AtomicLong对象中的偏移量,用于修改seed
    private static final long valueOffset;

构造方法

    /**
     * Creates a new random number generator. This constructor sets
     * the seed of the random number generator to a value very likely
     * to be distinct from any other invocation of this constructor.
     * 默认利用种子唯一标识符异或当前纳秒时间作为种子构造Random实例
     */
    public Random() {
        this(seedUniquifier() ^ System.nanoTime());
    }

	//种子唯一标识符生成器
    private static long seedUniquifier() {
        // L'Ecuyer, "Tables of Linear Congruential Generators of
        // Different Sizes and Good Lattice Structure", 1999
        for (;;) {
            long current = seedUniquifier.get();
            long next = current * 181783497276652981L;
            if (seedUniquifier.compareAndSet(current, next))
                return next;
        }
    }

	//种子唯一标识符
    private static final AtomicLong seedUniquifier
        = new AtomicLong(8682522807148012L);

    /**
     * Creates a new random number generator using a single {@code long} seed.
     * The seed is the initial value of the internal state of the pseudorandom
     * number generator which is maintained by method {@link #next}.
     *
     * <p>The invocation {@code new Random(seed)} is equivalent to:
     *  <pre> {@code
     * Random rnd = new Random();
     * rnd.setSeed(seed);}</pre>
     *
     * @param seed the initial seed
     * @see   #setSeed(long)
     */
    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);
        }
    }

生成随机数方法

nextInt

    public int nextInt() {
        return next(32);
    }

next

    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        //使用CAS来修改种子
        do {
            oldseed = seed.get();
            //某种算法,不懂
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

可以看出Random类共用一个种子,并且是使用CAS来更新种子,在高并发场景中CAS会导致大量的忙等待(CPU)占用,而且效率低下

Thread

相关属性

    //都使用了字节填充
	/** The current seed for a ThreadLocalRandom */
    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    /** Secondary seed isolated from public ThreadLocalRandom sequence */
    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;

ThreadLocalRandom

类图

avatar

属性

    //借助于Unsafe使用CAS机制来修改线程中的种子变量
	// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long SEED;
    private static final long PROBE;
    private static final long SECONDARY;
    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);
        }
    }

构造方法

        /** Constructor used only for static singleton */
	//饿汉式单例模式
    private ThreadLocalRandom() {
        initialized = true; // false during super() call
    }

    /** The common ThreadLocalRandom */
    static final ThreadLocalRandom instance = new ThreadLocalRandom();

	//获取实例
	public static ThreadLocalRandom current() {
        //如果线程探针为0那就对其初始化
        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);
    }

生成随机数方法

    public int nextInt(int origin, int bound) {
        if (origin >= bound)//边界校验
            throw new IllegalArgumentException(BadRange);
        return internalNextInt(origin, bound);
    }

	//获取随机数
    final int internalNextInt(int origin, int bound) {
        int r = mix32(nextSeed());
        if (origin < bound) {
            int n = bound - origin, m = n - 1;
            if ((n & m) == 0)
                r = (r & m) + origin;
            else if (n > 0) {
                for (int u = r >>> 1;
                     u + m - (r = u % n) < 0;
                     u = mix32(nextSeed()) >>> 1)
                    ;
                r += origin;
            }
            else {
                while (r < origin || r >= bound)
                    r = mix32(nextSeed());
            }
        }
        return r;
    }

	//更新并返回种子
	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;
    }

标签:ThreadLocalRandom,int,Random,long,seed,static,解析,源码
来源: https://www.cnblogs.com/redo19990701/p/13171939.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有