多线程并发编程5-ThreadLocalRandom类源码剖析

    今天来说一说ThreadLocalRandom类,ThreadLocalRandom类是juc包下的随机数生成器,它弥补了Random类的不足。下面介绍Random的源码实现、Random存在的问题以及ThreadLocalRandom是如何优化Random存在的缺陷的。

Random

    Random类是一个使用广泛的随机生成工具类,下面的代码相信大家一定不陌生,

Random random =new Random();

random.nextInt();

生成一个Random对象实例,通过next系列方法生成对应类型的随机数。next系列方法是如何生成随机数的呢?下面从源码来一探究竟。

nextInt

public int nextInt() {

return next(32);

}

nextInt方法里面调用的是next方法,这就是Random生成随机数的核心。

next

protected int next(int bits) {

long oldseed, nextseed;

    AtomicLong seed =this.seed;

    do {

oldseed = seed.get();    //(1)

        nextseed = (oldseed *multiplier +addend) &mask;    //(2)

    }while (!seed.compareAndSet(oldseed, nextseed));    //(3)

    return (int)(nextseed >>> (48 - bits));    //(4)

}

(1)获得当前种子的值,seed是AtomicLong类型,保证在多线程并发下只会有一个线程设置seed,关于AtomicLong类的原理可以点这里

(2)根据当前的种子值计算新的种子值。

(3)通过AtomicLong类的CAS算法方法设置seed为新计算的种子值,设置失败则通过自旋的方式知道设置成功为止。

(4)使用固定算法根据新的种子生成随机数。

    从上面的代码可以看到在多线性高并发的场景下多个线程同时竞争设置seed,但是设置seed的方法使用的CAS算法,只能有一个线程设置成功,而其他线程都需要通过自旋进行持续尝试直到设置成功。多个线程竞争同一个资源这就是会造成性能下降的原因,这个原因和前面介绍的原子操作类的缺陷是一样的。

ThreadLocalRandom

    上面介绍了高并发下Random性能下降的原因是因为多线程对一个资源的竞争,知道问题所在,ThreadLocalRandom解决这个缺陷的方法就是每个线程一个种子,这样避免的多个线程竞争同一个种子带来性能下降的问题。

    下面介绍一个ThreadLocalRandom类的使用案例,通过案例调用的方法作为我们源码剖析的切入点。

ThreadLocalRandom random = ThreadLocalRandom.current();

random.nextInt();

current

public static ThreadLocalRandomcurrent() {   

if (UNSAFE.getInt(Thread.currentThread(), PROBE) ==0)    //(1)

localInit();    

    return instance;

}

(1)获取当前线程的threadLocalRandomProbe本地变量,如果threadLocalRandomProbe为0,则对当前线程本地变量进行初始化。

localInit

static final void localInit() {

int p =probeGenerator.addAndGet(PROBE_INCREMENT);    //(1)

    int probe = (p ==0) ?1 : p; // skip 0

    long seed =mix64(seeder.getAndAdd(SEEDER_INCREMENT));    //(2)

    Thread t = Thread.currentThread();

    UNSAFE.putLong(t, SEED, seed);    //(3)

    UNSAFE.putInt(t, PROBE, probe);    //(4)

}

(1)通过CAS算法递增PROBE_INCREMENT变量,并将递增后的值赋值给p,之后利用该值为线程的本地变量threadLocalRandomSeed进行初始化赋值。

(2)通过CAS算法递增SEEDER_INCREMENT变量,并将递增前的值赋给seed,之后利用该值为线程的本地变量threadLocalRandomProbe进行初始化赋值。

(3)(4)为当前线程的threadLocalRandomSeed变量和threadLocalRandomProbe变量进行赋值,这里没有使用锁或是使用CAS进行赋值,是因为当前线程的本地变量不会被其他线程访问,不会造成内存可见性问题,也不会造成竞争问题。

    这里多提一点吧,localInit方法源码中可以看见仍然有用CAS+自旋的方法获取变量PROBE_INCREMENT和SEEDER_INCREMENT,但是这只有在获取ThreadLocalRandom实例的时候才会进行这两个变量的操作,所以在实现的代码中使用建议一个线程就获取一次ThreadLocalRandom实例,这样即使在高并发下也不会有什么性能的损失。

nextInt

public int nextInt() {

return mix32(nextSeed());    //(1)

}

(1)获取新的种子并使用固定算法获取随机值。

nextSeed

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

    return r;

}

(1)获取当前线程的本地变量threadLocalRandomSeed的值,加上常量GAMMA之后,重新设置当前线程的本地变量threadLocalRandomSeed的值。

    这里没有使用锁或是使用CAS进行赋值,是因为当前线程的本地变量不会被其他线程访问,不会造成内存可见性问题,也不会造成竞争问题。

    今天的分享就到这,有看不明白的地方一定是我写的不够清楚,所有欢迎提任何问题以及改善方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容