为什么要使用ThreadLocalRandom代替Random生成随机数
2017-12-17 15:12
597 查看
799
java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。
安全随机生成器
需要生成加密性强的随机数据的时候才用它
生成速度慢
如果需要生成大量的随机数据,可能会产生阻塞需要等待外部中断事件
而伪随机生成器,只依赖于“seed”的初始值,如果给生成算法提供相同的seed,可以得到一样的伪随机序列。一般情况下,由于它是计算密集型的(不依赖于任何IO设备),因此生成速度更快。以下是伪随机生成器的进化史。
java.util.Random
自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。
Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕,后面的测试结果中可以看到它的糟糕表现。
java.util.concurrent.ThreadLocalRandom
1.7增加该类,企图将它和Random结合以克服所有的性能问题,该类继承自Random。
ThreadLocalRandom的主要实现细节:
使用一个普通的long而不是使用Random中的AtomicLong作为seed
不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()
它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行
测试
下面进行5种测试:
一个单独的Random被N个线程共享
ThreadLocalRandom
Random[], 其中每个线程N使用一个数组下标为N的Random
Random[], 其中每个线程N使用一个数组下标为N * 2的Random
所有的测试都使用封装在RandomTask类里的方法,每个方案都说明了如何使用随机生成器。
总结:
任何情况下都不要在多个线程间共享一个Random实例,而该把它放入ThreadLocal之中
java7在所有情形下都更推荐使用ThreadLocalRandom,它向下兼容已有的代码且运营成本更低
java里有伪随机型和安全型两种随机数生成器,伪随机生成器根据特定公式将seed转换成新的伪随机数据的一部分,安全随机生成器在底层依赖到操作系统提供的随机事件来生成数据。
安全随机生成器
需要生成加密性强的随机数据的时候才用它
生成速度慢
如果需要生成大量的随机数据,可能会产生阻塞需要等待外部中断事件
而伪随机生成器,只依赖于“seed”的初始值,如果给生成算法提供相同的seed,可以得到一样的伪随机序列。一般情况下,由于它是计算密集型的(不依赖于任何IO设备),因此生成速度更快。以下是伪随机生成器的进化史。
java.util.Random
自1.0就已经存在,是一个线程安全类,理论上可以通过它同时在多个线程中获得互不相同的随机数,这样的线程安全是通过AtomicLong实现的。
Random使用AtomicLong CAS(compare and set)操作来更新它的seed,尽管在很多非阻塞式算法中使用了非阻塞式原语,CAS在资源高度竞争时的表现依然糟糕,后面的测试结果中可以看到它的糟糕表现。
java.util.concurrent.ThreadLocalRandom
1.7增加该类,企图将它和Random结合以克服所有的性能问题,该类继承自Random。
ThreadLocalRandom的主要实现细节:
使用一个普通的long而不是使用Random中的AtomicLong作为seed
不能自己创建ThreadLocalRandom实例,因为它的构造函数是私有的,可以使用它的静态工厂ThreadLocalRandom.current()
它是CPU缓存感知式的,使用8个long虚拟域来填充64位L1高速缓存行
测试
下面进行5种测试:
一个单独的Random被N个线程共享
ThreadLocal<Random>
ThreadLocalRandom
Random[], 其中每个线程N使用一个数组下标为N的Random
Random[], 其中每个线程N使用一个数组下标为N * 2的Random
所有的测试都使用封装在RandomTask类里的方法,每个方案都说明了如何使用随机生成器。
import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; public class Test_Random { private static final long COUNT = 10000000; private static final int THREADS = 2; public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("Shared Random"); testRandom(THREADS, COUNT); /*System.out.println("ThreadLocal<Random>"); testThreadLocal_Random(THREADS, COUNT); System.out.println("ThreadLocalRandom"); testThreadLocalRandom(THREADS, COUNT); System.out.println("Shared Random[] with no padding"); testRandomArray(THREADS, COUNT, 1); System.out.println("Shared Random[] with padding"); testRandomArray(THREADS, COUNT, 2);*/ } private static class RandomTask implements Runnable { private final Random rnd; protected final int id; private final long cnt; private final CountDownLatch latch; private RandomTask(Random rnd, int id, long cnt, CountDownLatch latch) { super(); this.rnd = rnd; this.id = id; this.cnt = cnt; this.latch = latch; } protected Random getRandom() { return rnd; } @Override public void run() { try { final Random r = getRandom(); latch.countDown(); latch.await(); final long start = System.currentTimeMillis(); int sum = 0; for (long j = 0; j < cnt; j++) { sum += r.nextInt(); } final long time = System.currentTimeMillis() - start; System.out.println("Thread #" + id + " Time = " + time / 1000.0 + " sec, sum = " + sum); } catch (InterruptedException e) {} } } private static void testRandom(final int threads, final long cnt) { final CountDownLatch latch = new CountDownLatch(threads); final Random r = new Random(100); for (int i = 0; i < threads; ++i) { final Thread thread = new Thread(new RandomTask(r, i, cnt, latch)); thread.start(); } } private static void testRandomArray(final int threads, final long cnt, final int padding) { final CountDownLatch latch = new CountDownLatch(threads); final Random[] rnd = new Random[threads * padding]; for (int i = 0; i < threads * padding; ++i) { rnd[i] = new Random(100); } for (int i = 0; i < threads; ++i) { final Thread thread = new Thread(new RandomTask(rnd[i * padding], i, cnt, latch)); thread.start(); } } private static void testThreadLocalRandom(final int threads, final long cnt) { final CountDownLatch latch = new CountDownLatch(threads); for (int i = 0; i < threads; ++i) { final Thread thread = new Thread(new RandomTask(null, i, cnt, latch) { @Override protected Random getRandom() { // TODO Auto-generated method stub return ThreadLocalRandom.current(); } }); thread.start(); } } private static void testThreadLocal_Random(final int threads, final long cnt) { final CountDownLatch latch = new CountDownLatch(threads); final ThreadLocal<Random> rnd = new ThreadLocal<Random>() { @Override protected Random initialValue() { // TODO Auto-generated method stub return new Random(100); } }; for (int i = 0; i < threads; ++i) { final Thread thread = new Thread(new RandomTask(null, i, cnt, latch) { @Override protected Random getRandom() { // TODO Auto-generated method stub return rnd.get(); } }); thread.start(); } } }
总结:
任何情况下都不要在多个线程间共享一个Random实例,而该把它放入ThreadLocal之中
java7在所有情形下都更推荐使用ThreadLocalRandom,它向下兼容已有的代码且运营成本更低
相关文章推荐
- 使用ThreadLocalRandom产生并发随机数
- Java中生成随机数Random、ThreadLocalRandom、SecureRandom、Math.random()
- java7新特性——使用ThreadLocalRandom产生并发随机数
- 生成随机数的类Random和ThreadLocalRandom
- 生成随机数的类Random和ThreadLocalRandom
- [疯狂Java]基础类库:Random(随机数生成)、ThreadLocalRandom(线程安全随机数生成)
- Java中生成随机数Random、ThreadLocalRandom、SecureRandom、Math.random()
- 生成随机数的类Random和ThreadLocalRandom
- java7新特性——使用ThreadLocalRandom产生并发随机数
- java7新特性——使用ThreadLocalRandom产生并发随机数
- java7新特性——使用ThreadLocalRandom产生并发随机数
- Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom
- 使用 RandomStringUtils 类来生成随机码/随机数
- 使用 RandomStringUtils 类来生成随机码/随机数
- 使用Math.random生成随机数
- Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom
- Random使用和生成某区间的随机数
- JavaSE8基础 Random 使用无参构造函数,即默认种子生成随机数
- 使用Random类生成指定范围的随机数
- [转]Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom