本文转载自微信公众号《一个程序员的成长》,程序员的成长作者。转载本文请联系程序员的成长公众号。1.Random类及其限制publicintnextInt(intbound){if(bound<=0)thrownewIllegalArgumentException(BadBound);//计算新种子intr=next(31);intm=bound-1;//根据新seed计算随机数if((bound&m)==0)//即boundisapowerof2r=(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();returninstance;}staticfinalvoidlocalInit(){intp=probeGenerator.addAndGet(PROBE_INCREMENT);intprobe=(p==0)?1:p;//skip0longseed=mix64(seeder.getAndAdd(SEEDER_INCREMENT));Threadt=Thread.currentThread();UNSAFE.putLong(t,SEED,seed);UNSAFE.putInt(t,PROBE,probe);}此方法用于创建ThreadLocalRandom随机数生成器。如果当前线程中threadLocalRandomProbe的变量值为0,表示第一次调用当前方法,然后调用localInit方法初始化种子变量。这里使用延迟初始化。在localInit方法中并没有初始化seed变量,而是在需要生成随机数的时候生成seed变量。这是一个优化。staticfinalvoidlocalInit(){intp=probeGenerator.addAndGet(PROBE_INCREMENT);intprobe=(p==0)?1:p;//skip0longseed=mix64(seeder.getAndAdd(SEEDER_INCREMENT));Threadt=Thread.currentThread();不安全。putLong(t,SEED,seed);UNSAFE.putInt(t,PROBE,probe);}finallongnextSeed(){Threadt;longr;//readandupdateper-threadseed//生成新的种子(获取当前线程种子+种子增量)UNSAFE.putLong(t=Thread.currentThread(),SEED,r=UNSAFE.getLong(t,SEED)+GAMMA);returnr;}mix32是固定算法,这里重点关注nextSeed方法,第一次调用时time此时初始化,获取当前线程的threadLocalRandomSeed的值(第一次默认为0)+seedincrement,如果不是第一次,则获取旧seed的值+seedincrement为生成一个新的种子变量并将其设置回去。这样在多线程环境下就避免了竞争,因为threadLocalRandomSeed是Thread的一个变量,属于线程级别。
