, 即第0项为0 ,第一项为1,则第二项为 0+1=1
,以此类推 。我们最初的解决方法就是使用递归来解决,如下计算第n项的数值:private int num(int num){if (num <= 1){return num;}num = num(num-1) + num(num -2);return num;}
- 从上面简单代码中可以看到,当
n<=1
时返回n
, 如果n>1
则计算前一项的值f1
,在计算前两项的值f2
, 再将两者相加得到结果,这就是典型的递归问题,也是对应我们的ForkJoin
的工作模式,如下所示,根节点产生子任务,子任务再次衍生出子子任务,到最后在进行整合汇聚,得到结果 。
文章插图
- 我们通过
ForkJoinPool
来实现斐波那契数列的计算,如下展示:
/** * @url: i-code.online * @author: AnonyStar * @time: 2020/11/2 10:01 */public class ForkJoinApp3 {public static void main(String[] args) throws ExecutionException, InterruptedException {ForkJoinPool pool = new ForkJoinPool();//计算第二是项的数值final ForkJoinTask<Integer> submit = pool.submit(new Fibonacci(20));// 获取结果,这里获取的就是异步任务的最终结果System.out.println(submit.get());}}class Fibonacci extends RecursiveTask<Integer>{int num;public Fibonacci(int num){this.num = num;}@Overrideprotected Integer compute() {if (num <= 1) return num;//创建子任务Fibonacci subTask1 = new Fibonacci(num - 1);Fibonacci subTask2 = new Fibonacci(num - 2);// 执行子任务subTask1.fork();subTask2.fork();//获取前两项的结果来计算和return subTask1.join()+subTask2.join();}}
- 通过
ForkJoinPool
可以极大的发挥多核处理器的优势,尤其非常适合用于递归的场景,例如树的遍历、最优路径搜索等场景 。
- 上面说的是
ForkJoinPool
的使用上的,下面我们来说一下其内部的构造,对于我们前面说的几种线程池来说,它们都是里面只有一个队列,所有的线程共享一个 。但是在ForkJoinPool
中,其内部有一个共享的任务队列,除此之外每个线程都有一个对应的双端队列Deque
, 当一个线程中任务被Fork
分裂了,那么分裂出来的子任务就会放入到对应的线程自己的Deque
中,而不是放入公共队列 。这样对于每个线程来说成本会降低很多,可以直接从自己线程的队列中获取任务而不需要去公共队列中争夺,有效的减少了线程间的资源竞争和切换 。
文章插图
- 有一种情况,当线程有多个如
t1,t2,t3...
,在某一段时间线程t1
的任务特别繁重,分裂了数十个子任务,但是线程t0
此时却无事可做,它自己的deque
队列为空,这时为了提高效率,t0
就会想办法帮助t1
执行任务,这就是“work-stealing
”的含义 。 - 双端队列
deque
中,线程t1
获取任务的逻辑是后进先出,也就是LIFO(Last In Frist Out)
,而线程t0
在“steal
”偷线程t1
的deque
中的任务的逻辑是先进先出,也就是FIFO(Fast In Frist Out)
,如图所示,图中很好的描述了两个线程使用双端队列分别获取任务的情景 。你可以看到,使用 “work-stealing
” 算法和双端队列很好地平衡了各线程的负载 。
文章插图
本文由AnonyStar 发布,可转载但需声明原文出处 。
欢迎关注微信公账号 :云栖简码 获取更多优质文章
更多文章关注笔者博客 :云栖简码 i-code.online
推荐阅读
- python之最详细字符串篇
- 什么鱼凶猛 观赏鱼中最凶猛的鱼是哪种
- Spring Boot中一接多口实现
- 如何挑选三文鱼
- 如何挑选薄荷
- 如何挑选鲅鱼
- 北方茶市的重要参照,走进中国近海第茶―御海湾
- 生活中常见事物蕴含的道理 生活中的事物
- 烟雨江湖主线任务攻略 烟雨江湖中的支线任务攻略大全
- 龙井茶用中投法泡吗,想喝好的龙井茶