Java ThreadLocalRandom与共享静态随机实例性能比较测试
在我们的一个任务项目中,我们使用静态随机实例来实现随机数生成目标。Java7发布后,出现了新的ThreadLocalRandom类,用于生成随机数 从规格: 如果适用,在并发程序中使用ThreadLocalRandom而不是共享随机对象通常会遇到更少的开销和争用。当多个任务(例如,每个任务都是ForkJoinTask)在线程池中并行使用随机数时,使用ThreadLocalRandom尤其合适 而且: 当所有用法都是这种形式时,不可能在多个线程之间意外共享ThreadLocalRandom 所以我做了一个小测试:Java ThreadLocalRandom与共享静态随机实例性能比较测试,java,multithreading,performance,random,thread-local,Java,Multithreading,Performance,Random,Thread Local,在我们的一个任务项目中,我们使用静态随机实例来实现随机数生成目标。Java7发布后,出现了新的ThreadLocalRandom类,用于生成随机数 从规格: 如果适用,在并发程序中使用ThreadLocalRandom而不是共享随机对象通常会遇到更少的开销和争用。当多个任务(例如,每个任务都是ForkJoinTask)在线程池中并行使用随机数时,使用ThreadLocalRandom尤其合适 而且: 当所有用法都是这种形式时,不可能在多个线程之间意外共享ThreadLocalRandom 所以我
public class ThreadLocalRandomTest {
private static final int THREAD_COUNT = 100;
private static final int GENERATED_NUMBER_COUNT = 1000;
private static final int INT_RIGHT_BORDER = 5000;
private static final int EXPERIMENTS_COUNT = 5000;
public static void main(String[] args) throws InterruptedException {
System.out.println("Number of threads: " + THREAD_COUNT);
System.out.println("Length of generated numbers chain for each thread: " + GENERATED_NUMBER_COUNT);
System.out.println("Right border integer: " + INT_RIGHT_BORDER);
System.out.println("Count of experiments: " + EXPERIMENTS_COUNT);
int repeats = 0;
int workingTime = 0;
long startTime = 0;
long endTime = 0;
for (int i = 0; i < EXPERIMENTS_COUNT; i++) {
startTime = System.currentTimeMillis();
repeats += calculateRepeatsForSharedRandom();
endTime = System.currentTimeMillis();
workingTime += endTime - startTime;
}
System.out.println("Average repeats for shared Random instance: " + repeats / EXPERIMENTS_COUNT
+ ". Average working time: " + workingTime / EXPERIMENTS_COUNT + " ms.");
repeats = 0;
workingTime = 0;
for (int i = 0; i < EXPERIMENTS_COUNT; i++) {
startTime = System.currentTimeMillis();
repeats += calculateRepeatsForTheadLocalRandom();
endTime = System.currentTimeMillis();
workingTime += endTime - startTime;
}
System.out.println("Average repeats for ThreadLocalRandom: " + repeats / EXPERIMENTS_COUNT
+ ". Average working time: " + workingTime / EXPERIMENTS_COUNT + " ms.");
}
private static int calculateRepeatsForSharedRandom() throws InterruptedException {
final Random rand = new Random();
final Map<Integer, Integer> counts = new HashMap<>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = rand.nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
thread.start();
thread.join();
}
int repeats = 0;
for (Integer value : counts.values()) {
if (value > 1) {
repeats += value;
}
}
return repeats;
}
private static int calculateRepeatsForTheadLocalRandom() throws InterruptedException {
final Map<Integer, Integer> counts = new HashMap<>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = ThreadLocalRandom.current().nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
thread.start();
thread.join();
}
int repeats = 0;
for (Integer value : counts.values()) {
if (value > 1) {
repeats += value;
}
}
return repeats;
}
对于我来说,这有点奇怪,我希望使用ThreadLocalRandom时,与共享的随机实例相比,速度至少会有所提高,但没有看到任何区别
有人能解释为什么会这样,也许我不明白什么。提前感谢。您的测试代码有一个缺陷。到处都是标杆的祸根
thread.start();
thread.join();
为什么不保存LOC并写入
thread.run();
结果是一样的
编辑:如果您没有意识到上面的结果,这意味着您正在运行单线程测试,没有多线程运行。您没有并行运行任何东西,因为您正在等待每个线程在启动后立即完成。在启动线程的循环外需要一个等待循环:
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = rand.nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
threads.add(thread);
thread.start();
}
for (Thread thread: threads) {
thread.join();
}
List threads=new ArrayList();
对于(int i=0;i
也许只需看看实际发生的情况就更容易了。以下是ThreadLocal.get()
的源代码,它也被ThreadLocalRandom.current()
调用
其中,ThreadLocalMap是一个具有优化功能的类似HashMap的专用实现
所以基本上发生的是ThreadLocal持有一个映射线程->对象-或者在本例中是线程->随机-然后被查找并返回或创建。因为这并不是什么“神奇的”,所以计时将等于HashMap查找+要返回的实际对象的初始创建开销。由于HashMap查找(在此优化情况下)是线性的,因此查找的成本为k,其中k是哈希函数的计算成本
所以你可以做一些假设:
- ThreadLocal将比每次在每个Runnable中创建对象都要快,除非创建成本比k小得多。所以随机查找是一件好事,在里面放一个int可能不是那么聪明
- ThreadLocal将比使用您自己的HashMap更好,因为可以假设这样的通用实现等于k或更差
- ThreadLocal将比使用成本
myRandoms[threadID]
但是,这假设您首先知道哪些线程将处理您的工作,因此无论如何,这不是ThreadLocal的真正候选对象
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = rand.nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
threads.add(thread);
thread.start();
}
for (Thread thread: threads) {
thread.join();
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}