• 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种测试:

  1. 一个单独的Random被N个线程共享
  2. ThreadLocal<Random>
  3. ThreadLocalRandom
  4. Random[], 其中每个线程N使用一个数组下标为N的Random
  5. 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代替Random生成随机数的更多相关文章

  1. C# random生成随机数全部一样

    最近做排序测试  使用random生成随机数全部一样 估计是因为random采用的随机种子为时间戳 而一个循化执行消耗的时间没有到时间戳的最小单位 故没有变化 Thread.Sleep(10); 使用 ...

  2. centos 阶段复习 2015-4-6 dd命令 hosts.allow和hosts.deny 啊铭的myssh脚本 清空history命令历史 /dev/zero 零发生器 /dev/null 黑洞 /dev/random 生成随机数 第十一节课

    centos 阶段复习 2015-4-6  dd命令 hosts.allow和hosts.deny 啊铭的myssh脚本 清空history命令历史  /dev/zero 零发生器  /dev/nul ...

  3. 【转载】python 模块 - random生成随机数模块

    随机数种子 要每次产生随机数相同就要设置种子,相同种子数的Random对象,相同次数生成的随机数字是完全相同的: random.seed(1) 这样random.randint(0,6, (4,5)) ...

  4. Python3使用random生成随机数

    本文介绍使用Python3中的random库生成随机数.随机小数.随机序列.随机字符串以及扑克洗牌等方法. 一.生成随机浮点数或小数 1.#生成0-1之间的浮点数 import random rnd ...

  5. Random 生成随机数

    Random类 (java.util) Random类中实现的随机算法是伪随机,也就是有规则的随机.在进行随机时,随机算法的起源数字称为种子数(seed),在种子数的基础上进行一定的变换,从而产生需要 ...

  6. Python random() 生成随机数

    random() 函数中常见的函数如下: #!/usr/bin/python # -*- coding: UTF-8 -*- import random print( random.randint(1 ...

  7. C# Random循环生成随机数重复问题解决方案

    C# Random循环生成随机数重复问题解决方案1.当我们通过Random生成随机数时,习惯的写法如下: int a=new Random().Next(0,100); 然后生成一个数据数没有任何问题 ...

  8. 生成随机数(Random类)和获取用户输入(Scanner类)

    生成指定范围内的随机数 Math.random() 生成随机数,随机数在0到1之间,类型是 double. public class randCase { public static void mai ...

  9. 关于用 random 生成伪随机数的一个手笔

    我在想还要不要写什么文字.确实不需要太多的文字描述吧. 前奏插一个小话题,之前在网上看到这样的冷笑话(有图的),一个程序猿调试个程序,早上怀疑某某地方的错误,下午怀疑某某地方的错误,晚上怀疑某某地方可 ...

随机推荐

  1. 【公开课】【阿里在线技术峰会】何登成:AliSQL性能优化与功能突破的演进之路

    MySQL的公开课,可能目前用不上这些,但是往往能在以后想解决方案的时候帮助到我.以下是阿里对公开课的整理 摘要: 本文根据阿里高级数据库专家何登成在首届阿里巴巴在线技术峰会上的分享整理而成.他主要介 ...

  2. 在android C/C++ native编程(ndk)中使用logcat

    最近在研究Android 2.2 源代码的C/C++层,需要对代码进行一些调试,但是奇怪的是,直接添加LOGD("XXXXXXXX");,使用logcat却看不到任何输出,换成LO ...

  3. Android Preference详解

    转载请标明出处:ttp://blog.csdn.net/sk719887916/article/details/42437253 Preference 用来管理应用程序的偏好设置和保证使用这些的每个应 ...

  4. Cocos2d-swift V3.x 中的update方法

    在cocos2d V3.x中update方法如果实现,则会被自动调用;不用向早期的版本那样要显式schedule. 但是你还是要显式schedule其他方法或blocks使用node的schedule ...

  5. Leetcode_217_Contains Duplicate

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/46271159 Given an array of inte ...

  6. 面试之路(29)-TCP流量控制和拥塞控制-滑动窗口协议详解

    拥塞: 拥塞发生的主要原因在于网络能够提供的资源不足以满足用户的需求,这些资源包括缓存空间.链路带宽容量和中间节点的处理能力.由于互联网的设计机制导致其缺乏"接纳控制"能力,因此在 ...

  7. Linux文件系统及常用命令

    Linux文件系统介绍: 一 .Linux文件结构 文件结构是文件存放在磁盘等存贮设备上的组织方法.主要体现在对文件和目录的组织上.目录提供了管理文件的一个方便而有效的途径. Linux使用树状目录结 ...

  8. Jenkins hash

    最早,Bob Jenkins提出了多个基于字符串通用Hash算法(搜Jenkins Hash就知道了),而Thomas Wang在Jenkins的基础上,针对固定整数输入做了相应的Hash算法.其64 ...

  9. C# PDF Page操作——设置页面切换按钮

    概述 在以下示例中,将介绍在PDF文档页面设置页面切换按钮的方法.示例中将页面切换按钮的添加分为了两种情况,一种是设置按钮跳转到首页.下页.上页或者最后一页,另一种是设置按钮跳转到指定页面.两种方法适 ...

  10. PLSQL 创建自定义函数注意事项

    2017-6-8周四,今天遇到的需求是,从数据库中查找出某张表的某些数据,并将这些数据做简单的加减运算再得到结果集,没有思路,后来问辉哥,给我的建议是给这些运算封装成一个SQL函数,select选择字 ...