|
|
51CTO旗下网站
|
|
移动端

1.7.13 fork/join框架

《Java多线程与Socket:实战微服务框架》第1章多线程基础,本书尽量采用简要和通俗易懂的方式来介绍多线程知识。本节为大家介绍fork/join框架。

作者:庞永华来源:电子工业出版社|2019-03-26 16:04

1.7.13 fork/join框架

为充分利用CPU资源,提高多线程/多进程的并发能力,提高任务的处理速度,我们可以把一个大任务拆分成若干子任务,交给多个线程来处理,然后将每个线程的输出结果进行合并,获得大任务的最终处理结果。这种处理方式被称为“fork/join”(见图1-3)。

Java 7提供了一个fork/join框架的实现。我们来看一个示例:

  1. public class C1_7_13 extends RecursiveTask<Integer> {  
  2. private static final long serialVersionUID = -4925866221809716545L;  
  3. private static final Logger log = LoggerFactory.getLogger(C1_7_13.class);  
  4. protected int start;  
  5. protected int end;  
  6. public C1_7_13(int start, int end) {  
  7. this.start = start;  
  8. this.end = end;  
  9. }  
  10. @Override  
  11. protected Integer compute() {  
  12. // 声明计算用的变量  
  13. int m = 2000s = startn = endr = 0;  
  14. // 创建子任务列表,每个子任务处理m个数字  
  15. List<ForkJoinTask<Integer>> lt = new ArrayList<ForkJoinTask<Integer>>();  
  16. do {  
  17. if (n - s < m * 1.5f) {  
  18. for (int i = s; i <= n; i++) {  
  19. r += i;  
  20. }  
  21. log.info("Sum {}~{} = {}", s, n, r);  
  22. } else {  
  23. n = Math.min(s + m - 1, n);  
  24. lt.add(new C1_7_13(s, n).fork());  
  25. }  
  26. s = n + 1;  
  27. n = end;  
  28. } while (s <= end);  
  29. // 合并子任务的结果  
  30. for (ForkJoinTask<Integer> t : lt) {  
  31. r += t.join();  
  32. }  
  33. return r;  
  34. }  
  35. public static void main(String[] args) {  
  36. ForkJoinPool fjp = new ForkJoinPool();  
  37. // 计算从ss到nn的累加  
  38. int ss = 1nn = 10001;  
  39. Future<Integer> result = fjp.submit(new C1_7_13(ss, nn));  
  40. try {  
  41. System.out.println(result.get());  
  42. } catch (InterruptedException e) {  
  43. e.printStackTrace();  
  44. } catch (ExecutionException e) {  
  45. e.printStackTrace();  
  46. }  
  47. }  

以上代码中的ForkJoinPool是专用于执行ForkJoinTask的线程池。而ForkJoinTask被用于封装可拆分的任务对象,它的两个子类RecursiveAction和RecursiveTask分别用于有返回值的任务对象和没有返回值的任务对象。

值得一提的是,为了提高多线程的处理效率,避免出现线程饥饿,Java的fork/join框架采用了一种名为“工作窃取”的方法。每个线程都有自己的双端任务队列,一般情况下,线程总是从队列头获取任务。当某个线程的任务队列为空时,它会尝试从其他线程任务队列的尾部“窃取”任务来执行。

图1-4中的线程2“窃取”了线程1来不及处理的任务。此方法较好地均衡了线程间的任务分配,提高了并行计算的能力,同时减少了线程间的竞争。其缺点是需要创建双端队列,插入任务和“窃取”任务存在锁竞争。


喜欢的朋友可以加入官方的读书群

51CTO读书频道二维码


51CTO读书会第9群:808517103

【责任编辑:book TEL:(010)68476606】

回书目   上一节   
点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

Jenkins Pipeline可持续化集成

Jenkins Pipeline可持续化集成

优化运维流水线
共3章 | youerning

198人订阅学习

笑熬浆糊之职场那些事

笑熬浆糊之职场那些事

IT人的职场心法
共22章 | Bear_Boss

79人订阅学习

Redis运维秘籍

Redis运维秘籍

运维标配技术
共15章 | one叶孤舟

199人订阅学习

读 书 +更多

超级网管员——网络服务

本书全面介绍了Windows Server 2003 R2中最常用的各种服务,包括域名服务、动态IP地址服务、Windows名称服务、活动目录服务、Web服务、FTP...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO播客