从JDK 7 开始引进了一个新的伪随机数生成器,ThreadLocalRandom,从名称可看出是一个与线程相关的Random,和之前的Random进行对比,ThreadLocalRandom在性能上和多线程并发处理上做了一些改进。
1,sun.misc.Unsafe
由于在产生伪随机数过程中,Random和ThreadLocalRandom都使用到了一个特殊的包:sun.misc.Unsafe。先对此包作个简单介绍。
sun.misc.Unsafe在JDK源代码中,有多处出现。为了更高效和更简单,它提供了一些与平台相关的更底层的功能,即提供了JNI的某些简单功能的替代,Unsafe的大部分方法都是Native方法。从名称也可以看出,它提供的是从Java意义上来说是“不安全”的一些功能,因此不应该在Java核心类库之外使用。实际上,除了通过反射机制,我们也无法获取到Unsafe的实例,使用它的功能。
更具体的介绍,可参看:http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe。
2,理解Random:
从Random的介绍中可看出,一个Random类的实例可用于生成一个伪随机数。生成伪随机数的过程,是指定一个seed,然后通过一个公式来生成一个新的seed,并且根据新的seed值得到伪随机数。而如果两个不同的Random实例,指定的seed相同,则生成的伪随机数也相同。
代码展示: seed相同,则生成的随机数相同;
public static void main(String[] args) { // TODO Auto-generated method stub int currentseed = 100; System.out.println("current seed:" + currentseed); Random r1 = new Random(currentseed); int value1 = r1.nextInt(); Random r2 = new Random(currentseed); int value2 = r2.nextInt(); System.out.println("Random Object: " + r1.hashCode() + " AND " + r2.hashCode()); System.out.println("get random int:" + value1 + "," + value2); }
运行结果如下:
current seed:100 Random Object: 1311053135 AND 118352462 get random int:-1193959466,-1193959466
通过运行结果可以看出,相同的seed,不同的Random对象,生成的随机数是一样的。
在多个线程使用多个Random实例时,如果指定的seed是相同的,则生成的伪随机数序列也是相同的。因此需要使用不同的seed来创建Random实例,比如使用Random默认的seed,或使用当前的时间System.currentTimeMillis();
代码展示:多线程中,seed相同,则生成的随机数相同;
public static void main(String[] args) { // TODO Auto-generated method stub int currentseed = 100; System.out.println("current seed:" + currentseed); RandomThread rThread1 = new RandomThread(currentseed); new Thread(rThread1).start(); RandomThread rThread2 = new RandomThread(currentseed); new Thread(rThread2).start(); } public class RandomThread implements Runnable { private int seed; public RandomThread(int seed) { super(); this.seed = seed; } public void run() { // TODO Auto-generated method stub Random r1 = new Random(seed); try { for(int k =0 ;k<5;k++){ int value1 = r1.nextInt(); System.out.println("get random int" + k + ":" + value1); Thread.sleep(200); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
current seed:100 get random int0:-1193959466 get random int0:-1193959466 get random int1:-1139614796 get random int1:-1139614796 get random int2:837415749 get random int2:837415749 get random int3:-1220615319 get random int3:-1220615319 get random int4:-1429538713 get random int4:-1429538713
在两个线程中,实例化两个Random,使用相同的seed,则产生了完全相同的两个随机数序列。
从Random中获取随机数的源代码:
protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); } private static final long multiplier = 0x5DEECE66DL; private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1;
,从源代码中可以看出,在生成随机数时,使用的公式:
nextseed = (oldseed * multiplier + addend) & mask;
其中,oldseed则为传进去的初始seed。而其他三个参数,multiplier,addend和mask,都为类静态变量。因此即使是不同的Random实例,生成的nextseed值也是相同的,所得的的随机数也就是相同的。
因此如果要确保随机数的效果,可以在多个线程中,使用同一个Random实例,这样就只需要维护同一套seed值。而Random实例生成随机数时对seed值的更新是一个原子操作,因此多个线程可能会在此产生阻塞而影响并发性能。
获取随机数时的最后一行操作,更新seed值,是一个原子操作,AtomicLong类中直接通过调用unsafe.compareAndSwapLong方法完成。
private final AtomicLong seed; //seed值设置成了final,当初始化seed值之后,再不能通过Java代码来修改seed值。 protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); } //seed.compareAndSet(oldseed, nextseed) 操作,调用unsafe的方法。
public final boolean compareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); }
其中 seed.compareAndSet(oldseed, nextseed),调用unsafe的方法来完成。该方法通过获得seed的内存位置偏移量,来设置seed的值,用nextseed值替换oldseed值。注意seed变量是设置成final的,因此无法通过Java程序再修改seed值,在此通过unsafe调用native方法来完成seed值的更新。
代码展示多个线程共享同一个random实例时,从程序结果看,有一部分获取随机数的操作,花费的时间比较多,说明多线程使用同一Random时,容易产生阻塞:
public static void main(String[] args) { // TODO Auto-generated method stub int currentseed = 100; Random currRandom = new Random(currentseed); for(int k=0;k<20;k++){ RandomThread rThread1 = new RandomThread(currRandom); new Thread(rThread1).start(); } } public class RandomThread implements Runnable { private Random currentRandom; public RandomThread(Random random) { super(); this.currentRandom = random; } public void run() { // TODO Auto-generated method stub try { for(int k =0 ;k<10;k++){ long currenttime = System.currentTimeMillis(); int value1 = currentRandom.nextInt(50); System.out.println("get random int" + k + ":" + value1); System.out.println("Cost time for random value:" + (System.currentTimeMillis() - currenttime)); Thread.sleep(100 * value1); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
从以上的分析,可总结出Random存在的几个问题:
生成多个实例,浪费资源;
相同的seed值,产生的伪随机数序列相同,效果不理想;
多个线程共享同一个实例时,会降低并发性能。
因此ThreadLocalRandom应运而生。
3, 理解ThreadLocalRandom:
针对Random存在的一些问题,ThreadLocalRandom有针对性的进行了改进。
ThreadLocalRandom是单例模式,系统只保存一个ThreadLocalRandom实例。
程序通过ThreadLocalRandom.current(); 获取ThreadLocalRandom实例。
public static ThreadLocalRandom current() { if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) localInit(); return instance; } /** * Initialize Thread fields for the current thread. Called only * when Thread.threadLocalRandomProbe is zero, indicating that a * thread local seed value needs to be generated. Note that even * though the initialization is purely thread-local, we need to * rely on (static) atomic generators to initialize the values. */ 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); }
获取实例时,针对每个线程,只在第一次进行本地变量的参数初始化,多个线程的seed值也都是不同的。因此多个线程使用ThreadLocalRandom,获得的随机数也不一样。
但是ThreadLocalRandom针对每一个线程,都保存一份变量值在本地内存,在第一次使用时,初始化各个变量值,并且赋值给每个变量。
在更新seed值时,也都是在操作本地内存,避免了多线程操作时,对变量值的同步,大大提高了并发性能。
我们可以看到,在ThreadLocalRandom中定义了一些变量,而这些变量的值,是通过Thread线程保存在本地,即每个线程都保存了一份变量:
//ThreadLocalRandom中各变量的定义,以及通过Unsafe,获取保存在本地内存中的地址。 // Unsafe mechanics 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; 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); } } //Thread类中对每个变量的定义: // The following three initially uninitialized fields are exclusively // managed by class java.util.concurrent.ThreadLocalRandom. These // fields are used to build the high-performance PRNGs in the // concurrent code, and we can not risk accidental false sharing. // Hence, the fields are isolated with @Contended. /** 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; //在ThreadLocalRandom中,是针对每个线程,进行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; }
在ThreadLocalRandom中,是针对每个线程,对该线程的变量seed进行读取和更新。不存在多个线程共享的问题,因此也不存在影响并发性能的问题。
相关推荐
在Java编程语言中,生成随机数是常见的任务,这通常涉及到`java.util.Random`类和`java.lang.Math`类中的`random()`方法。...理解这两个方法的工作原理和用法,可以帮助开发者更有效地在程序中引入随机性。
本文将深入探讨如何在Java中实现...通过以上分析,我们不仅理解了如何在Java中实现随机输出1到32的功能,还了解了Java中随机数生成的基本原理,以及如何优化和扩展代码,这对于提升代码质量和功能多样性具有重要意义。
此外,`java.util.concurrent.ThreadLocalRandom`和`java.security.SecureRandom`分别提供了线程安全的随机数生成和更安全的随机数生成,适用于加密和其他安全性要求高的场景。 项目中的具体代码可能涵盖了各种随机...
在Java编程语言中,随机数的应用非常广泛,可以用于各种模拟、游戏开发、加密算法以及数据分析等场景。...在实际应用中,理解这些随机数生成器的工作原理和使用技巧,将有助于编写出更加高效和可靠的代码。
在Java中,我们通常会使用`Random`类或`ThreadLocalRandom`类来生成随机数,以确保每次抽奖的中奖概率均匀分布。 抽奖活动的实现方式多种多样,一种常见的方法是通过创建一个包含所有参与者的列表,然后随机选取一...
本文将详细解析摇号算法的核心概念、实现原理以及常见问题。 摇号算法的核心目标是保证随机性与公平性。随机性意味着每一个参与者被选中的概率相同,而公平性则要求算法不能被人为操控,确保结果的不可预测性。在...
4. **随机数生成**: 抽奖结果的不确定性需要通过随机数生成器实现,Java的`Random`类或者Android的`java.util.concurrent.ThreadLocalRandom`都可以用来生成随机的中奖位置。 5. **布局管理**: 项目可能包含了XML...
在这个话题下,我们将深入探讨如何使用Java的标准库`java.util.Random`类来生成随机数,并了解其工作原理。 `Random`类是Java提供的核心类,位于`java.util`包中,它提供了一系列方法来生成不同类型的随机数,如...
这需要理解和应用基本的数学原理和算法。 7. **分解质因数.java**:分解质因数是将一个合数写成几个质数的乘积。这个题目可能要求设计一个算法来分解输入的整数,需要对质数和因数分解有深入理解。 8. **瓷砖铺放....
Java中的伪随机数生成主要通过java.util.Random类实现。这个类使用线性同余生成算法来产生伪随机数。下面是Random类中生成伪随机数的关键知识点: 1. 构造函数:Random类提供了无参构造函数和带种子的构造函数。...
此外,`Random`类支持线程安全的实例化,如果需要在多线程环境下使用,可以通过`ThreadLocalRandom`类来获取线程局部的`Random`实例,避免了同步带来的性能开销。 在PrasannaJavaChallenge项目中,作者进一步扩展了...
5. **随机数生成**:地鼠的出现位置需要随机生成,这需要Java的`Random`类或者`ThreadLocalRandom`类来实现,以确保每次游戏的体验都有所不同。 6. **图形用户界面GUI**:游戏界面可能使用Java的AWT或Swing库创建,...
3. **随机数生成**:为了设置需要用户猜的数字,项目中会用到Java的`Random`类或者`ThreadLocalRandom`类来生成随机数。 4. **用户输入验证**:在用户提交猜测时,需要检查输入是否为有效数字,这可以通过正则...
例如,可以添加注释来解释代码的目的和工作原理,帮助其他开发者理解。 2. **异常处理**:获取本地主机名可能会抛出`UnknownHostException`,但在示例代码中没有进行捕获和处理。应当考虑添加异常处理机制,避免...
1. **随机数生成**:摇奖的核心是生成随机数字,Java提供了Random类或者更高级的ThreadLocalRandom和SecureRandom类来生成随机数。开发者可能根据摇奖规则设定一个特定范围,然后在这个范围内生成随机数。 2. **...
Java的`Random`类或者`ThreadLocalRandom`类可以用来生成这些随机值。 8. **数据结构和算法**:可能使用数组、链表或其他数据结构来存储和管理烟花的状态。同时,优化算法如搜索、排序或缓存策略可能被用来提高性能...
1. **生成随机数**:使用Random类或者ThreadLocalRandom类生成指定范围内的随机数。 2. **添加到集合**:将生成的随机数添加到集合中,如HashSet或TreeSet。 3. **去重与排序**:由于HashSet不会重复添加元素,而...
2. `BallGenerator.java` - 用于生成红球和蓝球的随机数类,可能使用了Java的`Random`类或`ThreadLocalRandom`类。 3. `LotteryTicket.java` - 投注票类,存储用户选择的红球和蓝球号码,以及投注相关信息。 4. `...
开发者可能使用`Random`类或`java.util.concurrent.ThreadLocalRandom`类来生成随机数,并根据奖品位置对应的区间来确定最终结果。 5. **音效播放**: 音效的添加增强了用户体验。Android的`MediaPlayer`类或`...
7. **随机数生成**:抽奖结果的随机性需要通过`Random`类或者更高级的`ThreadLocalRandom`来实现,理解随机数生成的原理和用法。 8. **多线程处理**:如果抽奖过程中涉及网络请求或较耗时的操作,可能需要使用`...