为什么要使用ThreadLocalRandom代替Random生成随机数
- 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个线程共享
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代替Random生成随机数的更多相关文章
- C# random生成随机数全部一样
最近做排序测试 使用random生成随机数全部一样 估计是因为random采用的随机种子为时间戳 而一个循化执行消耗的时间没有到时间戳的最小单位 故没有变化 Thread.Sleep(10); 使用 ...
- 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 ...
- 【转载】python 模块 - random生成随机数模块
随机数种子 要每次产生随机数相同就要设置种子,相同种子数的Random对象,相同次数生成的随机数字是完全相同的: random.seed(1) 这样random.randint(0,6, (4,5)) ...
- Python3使用random生成随机数
本文介绍使用Python3中的random库生成随机数.随机小数.随机序列.随机字符串以及扑克洗牌等方法. 一.生成随机浮点数或小数 1.#生成0-1之间的浮点数 import random rnd ...
- Random 生成随机数
Random类 (java.util) Random类中实现的随机算法是伪随机,也就是有规则的随机.在进行随机时,随机算法的起源数字称为种子数(seed),在种子数的基础上进行一定的变换,从而产生需要 ...
- Python random() 生成随机数
random() 函数中常见的函数如下: #!/usr/bin/python # -*- coding: UTF-8 -*- import random print( random.randint(1 ...
- C# Random循环生成随机数重复问题解决方案
C# Random循环生成随机数重复问题解决方案1.当我们通过Random生成随机数时,习惯的写法如下: int a=new Random().Next(0,100); 然后生成一个数据数没有任何问题 ...
- 生成随机数(Random类)和获取用户输入(Scanner类)
生成指定范围内的随机数 Math.random() 生成随机数,随机数在0到1之间,类型是 double. public class randCase { public static void mai ...
- 关于用 random 生成伪随机数的一个手笔
我在想还要不要写什么文字.确实不需要太多的文字描述吧. 前奏插一个小话题,之前在网上看到这样的冷笑话(有图的),一个程序猿调试个程序,早上怀疑某某地方的错误,下午怀疑某某地方的错误,晚上怀疑某某地方可 ...
随机推荐
- CRM导入组织报实例名称必须与计算机名称相同的问题
今天采用P2V拷贝了一台CRM数据库到虚机上,因为要加域必须得把计算机名改了,然后再重新导入组织的时候报错了:"实例名称必须与计算机名称相同",google了下没有匹配的问题答案, ...
- AngularJS进阶(十)AngularJS改变元素显示状态
AngularJS改变元素显示状态 前言 本文描述使用AngularJS提供的ng-show和ng-hide指令实现自动监听某布尔型变量来改变元素显示状态. 控制html元素显示和隐藏有n种方法:ht ...
- Gradle 1.12用户指南翻译——第二十四章. Groovy 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- DB Query Analyzer 6.03, the most excellent Universal DB Access tools on any Microsoft Windows OS
DB Query Analyzer 6.03, the most excellent Universal database Access tools on any Microsoft Wind ...
- ERP-非财务人员的财务培训教(三)------公司/部门预算编制与评价
一.预算管理中的行为问题 二.编制预算中的问题 三.经营计划与预算制度 第一节 经营目标 第二节 预算编制的内容及说明 第三节 推行预算制度的组织 第 ...
- iOS监听模式系列之推送消息通知
推送通知 和本地通知不同,推送通知是由应用服务提供商发起的,通过苹果的APNs(Apple Push Notification Server)发送到应用客户端.下面是苹果官方关于推送通知的过程示意图: ...
- Mybatis批量插入、批量更新
合理的使用批量插入.更新对优化有很大的作用,速度明显快了N倍. 数据库连接串后面要新增:&allowMultiQueries=true 批量插入的最大限制主要是看你整条sql占用的大小,所以可 ...
- 脚本自动化 ant
用ant,shell下也可以用make. 1,ant 是一个自动化编译工具,安装使用. 2,编写build.xml,核心,写明进行哪些操作,删,建,编译,javac,java等,从例子可以看出每个操作 ...
- iOS下如何获取一个类的所有子类
因为项目中,不同用户切换时,用的是不同数据库,路径不同,而JKDBModel,数据库创建和字段检测,在app一次生命周期里,只会执行一次,所以得考虑账号切换时,创建数据库,需要获取所有JKDBMode ...
- PS 色调——颜色运算
通过对三个通道定义不同的运算,使图像的色调改变,进而生成不同色彩的图像. clc; clear all; Image=imread('4.jpg'); Image=double(Image); R=I ...