使用多线程同时访问同一个Random实例发现的耗时问题,求解答

Ethan9987 2020-09-06 04:42:23
本人半小白。在学习《实战Java高并发程序设计》这本书时,跟着书写了一个例子,以下是代码。
在执行 代码 49~56行时,每个线程的执行时间在3000ms+,总执行时间 10000ms左右
本着简化代码的强迫症,把代码改成77~80行这个样子,但是每个线程执行时间变成了100ms左右,总执行时间400ms+。请问这是由于什么原因造成的呢?是简写的代码有问题吗?还是jdk(8)有相关优化?求赐教谢谢

public class RandomTimeTest {
// 要产生的随机数数量
public static final int GEN_COUNT = 10000000;
// 参与工作的线程数量
public static final int THREAD_COUNT = 4;
// 定义线程池
static ExecutorService exe = Executors.newFixedThreadPool(THREAD_COUNT);

public static Random rnd = new Random(123);
public static ThreadLocalRandom tlRnd = ThreadLocalRandom.current();

// 由ThreadLocal封装的Random
public static ThreadLocal<Random> tRnd = ThreadLocal.withInitial(() -> new Random(123));

public static class RndTask implements Callable<Long> {
private int mode = 0;

public RndTask(int mode) {
this.mode = mode;
}

// 创建随机数的方式
public Random getRandom() {
if (mode == 0)
return rnd;
else if (mode == 1)
return tRnd.get();
else if (mode == 2)
return tlRnd;
else
return null;
}

/**
* 每个线程产生若干随机数,完成工作后记录幷返回所消耗的时间
*/
@Override
public Long call() throws Exception {
long b = System.currentTimeMillis();
for (int i = 0; i < GEN_COUNT; i++)
getRandom().nextInt();
long e = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+ " spend " + (e - b) + "ms");
return e - b;
}
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
Future<Long>[] futures = new Future[THREAD_COUNT];
// 多线程共享Random实例
for (int i = 0; i < THREAD_COUNT; i++)
futures[i] = exe.submit(new RndTask(0));
long totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += futures[i].get();
System.out.println("多线程同时访问同一个Random实例:" + totaltime + "ms");


// 使用ThreadLocal情况
for (int i = 0; i < THREAD_COUNT; i++)
futures[i] = exe.submit(new RndTask(1));
totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += futures[i].get();
System.out.println("使用ThreadLocal包装Random实例: " + totaltime + "ms");

// 使用ThreadLocalRandom情况
for (int i = 0; i < THREAD_COUNT; i++)
futures[i] = exe.submit(new RndTask(2));
totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += futures[i].get();
System.out.println("使用ThreadLocalRandom实例: " + totaltime + "ms");
exe.shutdown();


/*long totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += exe.submit(new RndTask(0)).get();
System.out.println("多线程同时访问同一个Random实例:" + totaltime + "ms");

totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += exe.submit(new RndTask(1)).get();
System.out.println("使用ThreadLocal包装Random实例:" + totaltime + "ms");

totaltime = 0;
for (int i = 0; i < THREAD_COUNT; i++)
totaltime += exe.submit(new RndTask(2)).get();
System.out.println("使用ThreadLocalRandom实例:" + totaltime + "ms");
exe.shutdown();*/
}
}
...全文
2689 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
Ethan9987 2020-09-08
  • 打赏
  • 举报
回复
引用 5 楼 冰思雨 的回复:
然后,解释一下楼主的问题。 Random 的线程安全,采用的是 CAS 操作 。 这种操作,在线程频繁竞争的时候,效率反而会降低。 竞争越激烈,效率越低下,因为每次compare总是不相当,CPU的指令都浪费了,当然它是没有阻塞的原语操作。 能理解吗?就是那种疯狂的调用next函数,生成随机数的操作,所产生的问题。 如果多线程共用同一个Random对象,那么,线程数最好不要超过CPU的核心数,超过的数量越大,竞争越激烈,效率越低。 改用 ThreadLocal 比较好,不用担心线程间的竞争了,但是,线程数超过CPU核心数后,效率依然会有所降低,但是,要比共用的情况强的太多了。
我明白了 非常感谢!! 另外,非常不好意思,我结贴分给错楼层了
Ethan9987 2020-09-07
  • 打赏
  • 举报
回复
引用 1 楼 KeepSayingNo的回复:
感觉这个代码写得比较冗杂,如果是用多线程产生随机数,应该没有必要这样去写
感谢回复!我觉得书中是把这个当做一个例子帮助读者理解,或许是有些冗杂了。
KeepSayingNo 2020-09-07
  • 打赏
  • 举报
回复
感觉这个代码写得比较冗杂,如果是用多线程产生随机数,应该没有必要这样去写
冰思雨 2020-09-07
  • 打赏
  • 举报
回复
然后,解释一下楼主的问题。 Random 的线程安全,采用的是 CAS 操作 。 这种操作,在线程频繁竞争的时候,效率反而会降低。 竞争越激烈,效率越低下,因为每次compare总是不相当,CPU的指令都浪费了,当然它是没有阻塞的原语操作。 能理解吗?就是那种疯狂的调用next函数,生成随机数的操作,所产生的问题。 如果多线程共用同一个Random对象,那么,线程数最好不要超过CPU的核心数,超过的数量越大,竞争越激烈,效率越低。 改用 ThreadLocal 比较好,不用担心线程间的竞争了,但是,线程数超过CPU核心数后,效率依然会有所降低,但是,要比共用的情况强的太多了。
  • 打赏
  • 举报
回复
你简化代码反倒是没能使多线程发挥作用,,主线程一直在阻塞着挨个获取结果.
冰思雨 2020-09-07
  • 打赏
  • 举报
回复
你这77~91行的代码,和单线程跑一遍,有啥区别?

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧