• 爱吃回锅肉的瘦子
    2019-04-28
    https://www.liaoxuefeng.com/article/001493522711597674607c7f4f346628a76145477e2ff82000,老师,您好,我在廖雪峰网站中也看到forkjoin使用方式。讲解了,为啥不使用两次fork,分享出来给大家看看。

    作者回复: 用两次fork()在join的时候,需要用这样的顺序:a.fork(); b.fork(); b.join(); a.join();这个要求在JDK官方文档里有说明。

    如果是一不小心写成a.fork(); b.fork(); a.join(); b.join();就会有大神廖雪峰说的问题。

    建议还是用fork()+compute(),这种方式的执行过程普通人还是能理解的,fork()+fork()内部做了很多优化,我这个普通人看的实在是头痛。

    感谢分享啊。我觉得讲的挺好的。用这篇文章的例子理解fork()+compute()很到位。

     1
     35
  • 锦
    2019-04-27
    CPU同一时间只能处理一个线程,所以理论上,纯cpu密集型计算任务单线程就够了。多线程的话,线程上下文切换带来的线程现场保存和恢复也会带来额外开销。但实际上可能要经过测试才知道。

    作者回复: 👍

    
     26
  • QQ怪
    2019-05-24
    学习了老师的分享,现在就已经在工作用到了,的确是在同事面前好好装了一次逼

    作者回复: 👍说明你很有悟性😄

    
     15
  • 尹圣
    2019-04-29
    看到分治任务立马就想到归并排序,用Fork/Join又重新实现了一遍,
     /**
      * Ryzen 1700 8核16线程 3.0 GHz
      */
     @Test
     public void mergeSort() {
         long[] arrs = new long[100000000];
         for (int i = 0; i < 100000000; i++) {
             arrs[i] = (long) (Math.random() * 100000000);
         }
         long startTime = System.currentTimeMillis();
         ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
         MergeSort mergeSort = new MergeSort(arrs);
         arrs = forkJoinPool.invoke(mergeSort);
         //传统递归
         //arrs = mergeSort(arrs);
         long endTime = System.currentTimeMillis();
         System.out.println("耗时:" + (endTime - startTime));
     }
     /**
      * fork/join
      * 耗时:13903ms
      */
     class MergeSort extends RecursiveTask<long[]> {
         long[] arrs;
         public MergeSort(long[] arrs) {
             this.arrs = arrs;
         }
         @Override
         protected long[] compute() {
             if (arrs.length < 2) return arrs;
             int mid = arrs.length / 2;
             MergeSort left = new MergeSort(Arrays.copyOfRange(arrs, 0, mid));
             left.fork();
             MergeSort right = new MergeSort(Arrays.copyOfRange(arrs, mid, arrs.length));
             return merge(right.compute(), left.join());
         }
     }
     /**
      * 传统递归
      * 耗时:30508ms
      */
     public static long[] mergeSort(long[] arrs) {
         if (arrs.length < 2) return arrs;
         int mid = arrs.length / 2;
         long[] left = Arrays.copyOfRange(arrs, 0, mid);
         long[] right = Arrays.copyOfRange(arrs, mid, arrs.length);
         return merge(mergeSort(left), mergeSort(right));
     }
     public static long[] merge(long[] left, long[] right) {
         long[] result = new long[left.length + right.length];
         for (int i = 0, m = 0, j = 0; m < result.length; m++) {
             if (i >= left.length) {
                 result[m] = right[j++];
             } else if (j >= right.length) {
                 result[m] = left[i++];
             } else if (left[i] > right[j]) {
                 result[m] = right[j++];
             } else result[m] = left[i++];
         }
         return result;
     }
    展开

    作者回复: 👍👍👍举一反三了😄

     1
     12
  • 右耳听海
    2019-04-27
    请教老师一个问题,merge函数里的mr2.compute先执行还是mr1.join先执行,这两个参数是否可交换位置

    作者回复: 我觉得不可以,如果join在前面会先首先让当前线程阻塞在join()上。当join()执行完才会执行mr2.compute(),这样并行度就下来了。

     1
     8
  • linqw
    2019-04-27
    以前在面蚂蚁金服时,也做过类似的题目,从一个目录中,找出所有文件里面单词出现的top100,那时也是使用服务提供者,从目录中找出一个或者多个文件(防止所有文件一次性加载内存溢出,也为了防止文件内容过小,所以每次都确保读出的行数10万行左右),然后使用fork/join进行单词的统计处理,设置处理的阈值为20000。
    课后习题:单核的话,使用单线程会比多线程快,线程的切换,恢复等都会耗时,并且要是机器不允许,单线程可以保证安全,可见性(cpu缓存,单个CPU数据可见),线程切换(单线程不会出现原子性)

    作者回复: 👍

    
     7
  • Geek_ebda96
    2019-05-13
    如果所有的并行流计算都是 CPU 密集型计算的话,完全没有问题,但是如果存在 I/O 密集型的并行流计算,那么很可能会因为一个很慢的 I/O 计算而拖慢整个系统的性能。

    老师这里的意思是不是,如果有耗时的i/o计算,需要用单独的forkjoin pool 来处理这个计算,在程序设计的时候就要跟其他cpu密集计算的任务分开处理?

    作者回复: 是的

    
     4
  • Nick
    2019-06-05
    简易的MapReduce的程序跑下来不会栈溢出吗?

    作者回复: 递归程序,如果语言层面没有办法优化,都会的

    
     2
  • êwěn
    2019-04-27
    老师,fork是fork调用者的子任务还是表示下面new出来的任务是子任务?

    作者回复: fork是fork调用者这个子任务加入到任务队列里

    
     2
  • 木木匠
    2019-04-27
    单核cpu上多线程会导致线程的上下文切换,还不如单核单线程处理的效率高。
     1
     2
  • 王彬-Antonio
    2020-01-06
    老师,您在文中提到io密集型和计算密集型最好区分开不同线程池。假设两个线程池如果都在运行,它们之间怎么竞争CPU线程?

    作者回复: 操作系统负责调度,在操作系统眼里,都一样,都是线程

    
     1
  • 蓝天白云看大海
    2019-06-02
    join会阻塞线程吗?如果阻塞线程,而线程池里的线程个数又有线,那么递归几次之后所有线程不都全阻塞了吗!
    
     1
  • null
    2019-05-27
    @王伟童鞋的问题,我们也有这场景:通过手机号查询商家信息。
    我们是在 redis 里维护(手机号,商家号)关联关系,在 redis 里通过手机号查询商家号,就知道该去哪个库表查询商家具体信息了。

    内存开销:
    手机号,11 个字符,占用 11B;
    商家号,4 个字符,占用 4B;
    一条记录占用 15B,100 万条记录,就 15*100万B,大概是:
    15*1,000,000B/1000/1000=15M
    展开
    
     1
  • 张三
    2019-04-29
    ForkJoinTask这个抽象类的 fork() 和 join()底层是怎么实现的呢?
    
     1
  • 狂风骤雨
    2019-04-29
    好希望工作当中能有老师这样一位大牛,能为我答疑解惑

    作者回复: 我知道的就这些,都写出来了😂,显然我不是大牛😄

    
     1
  • 右耳听海
    2019-04-28
    这里用的递归调用,数据量大的时候会不会粘溢出,虽然这里用的二分,时间复杂度为logn

    作者回复: 我觉得会

    
     1
  • 朱晋君
    2019-04-28
    老师,请问为什么不能merge mr1.compute和mr2..compute或者mr1.join和mr2的join呢?

    作者回复: compute+compute相当于没用forkjoin,都在一个线程里跑的。如果用join+join也可以,不过jdk官方有个建议,顺序要用:a.fork(); b.fork(); b.join(); a.join();否则性能有问题。所以还是用fork+compute更简单。

    
     1
  • 王伟
    2019-04-28
    老师,我现在碰到一个生产问题:用户通过微信小程序进入我们平台,我们只能需要使用用户的手机号去我们商家库中查取该用户的注册信息。在只知道用户手机号的情况下我们需要切换到所有的商家库去查询。这样非常耗时。ps:我们商家库做了分库处理而且数量很多。想请教一下您,这种查询该如何做?

    作者回复: 可以加redis缓存看看,也可以加本地缓存。不要让流量直接打到数据库上

    
     1
  • 密码123456
    2019-04-28
    我记得之前提到过,使用线程数目大小的方法。如果io耗时过长可以多加线程数量,能够提升性能。如果io耗时过短,增加线程数量就不能,提升性能了?不知道是否能够对应,这个题目的答案?
    
     1
  • ban
    2019-04-27
    “如果存在 I/O 密集型的并行流计算,那么很可能会因为一个很慢的 I/O 计算而拖慢整个系统的性能。”

    老师这个问题,这句话前面的文字也看到,但是不太懂。如果共用一个线程池,但是不是有多个线程,如果一个线程操作I/O,应该不影响其他线程吧,其他线程还能继续执行,我不太理解为什么会拖慢整个系统,求老师帮我解答这个疑问。

    作者回复: 前提是有很多请求并发访问这个很慢的I/O计算,我们这的并发程序,往往都有很多请求同时访问的

    
     1
我们在线,来聊聊吧