ThreadLocalRandom源码解析

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多个线程会竞争同一个原子变量种子

ThreadLocalRandom源码解析

ThreadLocalRandom

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

ThreadLocalRandom源码解析

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

类图

ThreadLocalRandom源码解析

属性

    //借助于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;
    }
上一篇:【设计模式从入门到精通】14-访问者模式


下一篇:[LTMP搭建] Centos 6.5 安装配置 PHP