Random的升级版---ThreadlocalRandom

博客介绍了Random在日常开发中用于生成随机值,且是线程安全的,但多线程并发时自旋操作会增加CPU负担。而ThreadlocalRandom能有效降低压力,其实现原理与Threadlocal相似,多个线程相互隔离,还介绍了它的next()实现、实例获取方式及避免伪共享的注解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Random在日常开发过程中还是挺常用的,用于生成随机值;

 Random random = new Random();
 random.nextInt(100);
 /**
 *nextInt(100)实际调用的方法是next(int bits)
 *
 */
 protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
            //这里使用CAS,保证多线程情况下只有一个线程执行成功
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

从上面的源码看,可知Random生成随机数是线程安全的,但是在多线程并发的情况下,自旋操作会增加CPU的负担,而ThreadlocalRandom能够有效的降低这种压力;从名称上大致能够看出ThreadlocalRandom的实现原理与Threadlocal相似,多个线程相互隔离的。下面是ThreadlocalRandom的next()的实现。

public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        int r = mix32(nextSeed());
        int m = bound - 1;
        if ((bound & m) == 0) //bound是2次幂
            r &= m;
        else { // 不是2次幂
            for (int u = r >>> 1;
                 u + m - (r = u % bound) < 0;
                 u = mix32(nextSeed()) >>> 1) ;
        }
        return r;
    }

在ThreadlocalRandom里面重写了next方法,并未沿用Random中的cas原子性操作,随机数生成的种子seed并未放在当前类中,而是通过nextSeed()获取seed的。

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

而SEED常量初始化在创建实例的时候;ThreadlocalRandom的实例获取方式

ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();

通过静态代码块进行一些变量的初始化。

static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            //获取线程Thread中的threadLocalRandomSeed常量,作为seed
            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);
        }
    }

在ThreadLocalRandom实例化的时候将静态代码块中初始化的参数写进ThreadLocalRandom 类中;

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

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

其中加这个注解的原因@sun.misc.Contended,就是避免伪共享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值