ThreadLocalRandom类

java并发编程之美 笔记

ThreadLocalRandom是在jdk7之后在JUC包下新增的随机数生成器,补充了Random在多线程下的缺陷。在学习它之前,我们先学习下Random类。

Random类

创建APIs

public class Random implements java.io.Serializable {
 	private final AtomicLong seed;

    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L << 48) - 1;
    private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)

    //1.使用seed参数的 构造函数
    public Random() {
    	//1.1可以理解为 seed = f(seedUniquifier(),System.nanoTime()) ;通过“异或”,计算出seed
    	//1.2 计算出seed后,调用this(seed);
        this(seedUniquifier() ^ System.nanoTime());
    }

    //2.给定sedd参数,构造函数
    public Random(long seed) {
        if (getClass() == Random.class)
           //2.1 initialScramble(seed): 可以理解为 y = f(seed);
           //2.2 创建AtomicLong,并将初始值设置为 f(seed);
            this.seed = new AtomicLong(initialScramble(seed));
        else {
            this.seed = new AtomicLong();
            //调用setSeed(), 该方法可被sup-CLASS 覆盖重写;
            //默认的实现方法同上面if分支内逻辑.
            setSeed(seed);
        }
    }
   
    //2.1 将seed做位运算: y = f(seed); 拼凑新的seed
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }
	
	//setSeed()可由子类覆盖重写;
    synchronized public void setSeed(long seed) {
        this.seed.set(initialScramble(seed));
        haveNextNextGaussian = false;
    }
}

上述代码中,在使用无参的构造函数new Random()时,会使用seedUniquifier() ^ System.nanoTime()来计算出long seed,这里单独将seedUniquifier()拿出来分析下:

  • seedUniquifier()
  private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
  private static long seedUniquifier() {
      for (;;) {
          long current = seedUniquifier.get();
          //计算出新的seed,
          long next = current * 181783497276652981L;
         
         //循环尝试cas操作,更新seedUniquifier,当操作成功时,返回next;
          if (seedUniquifier.compareAndSet(current, next))
              return next;
      }
  }

获取随机数

public class Random implements java.io.Serializable {

 	//bound: 边界值,最大值;
	public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);

        //获取seed种子
        int r = next(31);
        int m = bound - 1;

        if ((bound & m) == 0) 
        	 //当bound为2的冥次方时,bound & m = bound & (bound -1) == 0
        	 //计算出随机数r,然后返回
            r = (int)((bound * (long)r) >> 31);
        else {
        	//计算出随机数r
            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 {
        	//获取旧的seed
            oldseed = seed.get();

            //根据旧的seed计算出新的seed,可以理解为:nextseed = f(oldseed);
            nextseed = (oldseed * multiplier + addend) & mask;
		
		//当多个线程并发执行时,不断执行CAS操作尝试,直至成功.
        } while (!seed.compareAndSet(oldseed, nextseed));

        //重新按位运算计算出最终的seed : 可以理解为  nextseed = f(nextseed)
        return (int)(nextseed >>> (48 - bits));
    }
 }
  • 出现的问题
    当多个线程同时计算随机数时,会同时执行seed.compareAndSet(oldseed, nextseed)竞争seed原子变量的更新操作,由于更新时CAS操作,它是线程安全的操作,此时只有一个线程会成功,所以会导致大量的线程在进行自旋尝试,这降低了并发性能,如下图:
    在这里插入图片描述为了解决这个问题,TheadLocalRandom应运而生。

TheadLocalRandom

TheadLocalRandom继承自Random类.
初始化

public class ThreadLocalRandom extends Random {
   	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;
            //记录threadLocalRandomSeed ,threadLocalRandomProbe,threadLocalRandomSecondarySeed 在各个thread实例中内存偏移量
            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);
        }
    }


	boolean initialized;
	private ThreadLocalRandom() {
        initialized = true;
    }

    static final ThreadLocalRandom instance = new ThreadLocalRandom();
    
	public static ThregetIntadLocalRandom current() {
    	//当前线程的 PROBE对应的threadLocalRandomProbe为0时,需要进行初始化
        if (UNSAFE.(Thread.currentThread(), PROBE) == 0)
        	//给当前线程初始化,
            localInit();
        return instance;
    }
    
	初始化
    private static final int PROBE_INCREMENT = 0x9e3779b9;
    private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
    
    private static final AtomicInteger probeGenerator = new AtomicInteger();
  	private static final AtomicLong seeder = new AtomicLong(initialSeed());

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

        //给线程t的 threadLocalRandomSeed ,threadLocalRandomProbe设置值..
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
    }
}

Thread相关属性

public class Thread implements Runnable {

     /**当前线程ThreadLocalRandom 的 seed */
    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    /** 当threadLocalRandomSeed初始化后,  threadLocalRandomProbe值不为0*/
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

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

nextInt()

public int nextInt(int bound) {
   if (bound <= 0)
         throw new IllegalArgumentException(BadBound);
     //获取seed
     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;
 }

private static final long GAMMA = 0x9e3779b97f4a7c15L;

 //获取线程t当前的seed,并更新
 final long nextSeed() {
     Thread t; long r; 
     //SEED增加GAMMA,作为新种子
     UNSAFE.putLong(t = Thread.currentThread(), SEED,
                    r = UNSAFE.getLong(t, SEED) + GAMMA);
     return r;
 }

这里nextSeed()函数获取种子解决了Random高并发获取随机数时CAS操作自旋等待影响性能的问题。
它的结局而方法简单来说,就是每个seed都保存在Thread的threadLocalRandomSeed属性中,它是线程私有的,这就避免了高并发的资源竞争问题,可以使用下图来解释:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值