当前位置: 首页 > 后端技术 > Java

ThreadLocalRandom类原理分析

时间:2023-04-01 23:50:02 Java

1、Random类及其局限性publicintnextInt(intbound){if(bound<=0)thrownewIllegalArgumentException(BadBound);//计算新种子intr=next(31);intm=bound-1;//根据新种子计算随机数if((bound&m)==0)//也就是说,bound是2的幂r=(int)((bound*(long)r)>>31);else{for(intu=r;u-(r=u%bound)+m<0;u=next(31));}returnr;}protectedintnext(intbits){longoldseed,nextseed;//这是一个原子变量AtomicLongseed=this.seed;do{//(1),获取旧种子oldseed=seed.get();//(2)、计算新种子nextseed=(oldseed*multiplier+addend)&mask;//(3),CAS操作更新旧种子}while(!seed.compareAndSet(oldseed,nextseed));return(int)(nextseed>>>(48-bits));}Random总结:面试:多线程下Random存在什么样的问题?每个Random实例都有一个原子种子变量,用于记录当前种子值。生成新的随机数时,需要在当前种子的基础上计算出一个新的种子,并更新种子变量。在多线程环境下,多个线程会竞争对同一个原子变量的更新操作。由于原子变量的更新是CAS操作,同一时刻只有一个线程会成功,会导致大量线程自旋重试,从而降低并发度。表现。可能出现的现象:如果并发请求很多,自旋锁不断重试,CPU会一直飙升。2.ThreadLocalRandompublicstaticThreadLocalRandomcurrent(){if(UNSAFE.getInt(Thread.currentThread(),PROBE)==0)localInit();返回实例;}staticfinalvoidlocalInit(){intp=probeGenerator.addAndGet(PROBE_INCREMENT);int探针=(p==0)?1:p;//跳过0长种子=mix64(seeder.getAndAdd(SEEDER_INCREMENT));线程t=Thread.currentThread();UNSAFE.putLong(t,种子,种子);UNSAFE.putInt(t,PROBE,probe);}此方法用于创建ThreadLocalRandom随机数生成器。如果当前线程中threadLocalRandomProbe的变量值为0,则表示第一次调用当前方法,则调用localInit方法初始化种子变量。这里使用延迟初始化。在localInit方法中并没有初始化seed变量,而是在需要生成随机数的时候生成seed变量。这是一个优化。publicintnextInt(intbound){if(bound<=0)thrownewIllegalArgumentException(BadBound);//生成种子intr=mix32(nextSeed());intm=bound-1;if((bound&m)==0)//两个r&=m的幂;else{//拒绝代表过多的候选人(intu=r>>>1;u+m-(r=u%bound)<0;u=mix32(nextSeed())>>>1);}returnr;}finallongnextSeed(){线程t;长r;//读取和更新每线程种子//生成一个新种子(获取当前线程种子+种子增量)UNSAFE.putLong(t=Thread.currentThread(),SEED,r=UNSAFE.getLong(t,SEED)+GAMMA);返回r;mix32是固定算法,这里重点关注nextSeed方法,当Initialize第一次调用时,获取当前线程的threadLocalRandomSeed的值(第一个默认值为0)+seed增量,如果不是第一次,获取旧种子的值+种子增量生成新的种子变量并设置回去。这样在多线程环境下就避免了竞争,因为threadLocalRandomSeed是Thread的一个变量,属于线程级别。

最新推荐
猜你喜欢