Java并发编程从入门到精通 - 第7章:Fork/Join框架

1、综述:化繁为简,分而治之;递归的分解和合并,直到任务小到可以接受的程度;
2、Future任务机制:
  Future接口就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果;必要时可以通过get方法获取执行结果,该方法会阻塞直到任务会返回结果;也就是说Future接口提供三种功能:判断任务是否完成、能够中断任务、能够获取任务执行结果;
  Future接口里面的常用方法;
3、FutureTask:
  FutureTask类是Future接口唯一的实现类;
  FutureTask类实现了Runnable接口和Future接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值;
  FutureTask的两个构造器;

 1 /**
 2  * FutureTask的使用
 3  */
 4 package thread06;
 5
 6 import java.util.concurrent.Callable;
 7 import java.util.concurrent.ExecutionException;
 8 import java.util.concurrent.FutureTask;
 9
10 public class FutureTaskTest01
11 {
12     public static void main(String[] args) throws InterruptedException, ExecutionException
13     {
14         MySon myson = new MySon("Thread Son1");
15         FutureTask<String> ft1 = new FutureTask<String>(myson);
16         new Thread(ft1).start();
17         // 如果调用了get()方法,那么只有得到返回结果后才会继续往下面执行
18         // 不调用get()方法(不想获取返回结果),直接继续往下走
19         System.out.println(ft1.get());
20
21         // 执行完指定线程,返回指定结果(22)
22         FutureTask<Integer> ft2 = new FutureTask<Integer>(new MyRun(), 22);
23         new Thread(ft2).start();
24         System.out.println("result_" + ft2.get());
25
26         System.out.println("Thread Main end!");
27     }
28 }
29
30 class MySon implements Callable<String>
31 {
32     private String name;
33
34     public MySon(String name)
35     {
36         this.name = name;
37     }
38
39     @Override
40     public String call() throws Exception
41     {
42         Thread.sleep(1000L);
43         System.out.println(name + "任务计算完成");
44         return "result_11";
45     }
46 }
47
48 class MyRun implements Runnable
49 {
50     @Override
51     public void run()
52     {
53         try
54         {
55             Thread.sleep(1000L);  // 模拟干活
56         } catch (InterruptedException e)
57         {
58             e.printStackTrace();
59         }
60
61         System.out.println("特定线程2完成任务");
62     }
63 }
64
65 /*
66 任务实现Callable接口,可以返回结果;
67 任务实现Runnable接口,不可以返回结果,但可以通过FutureTask变相返回指定结果;
68 */

FutureTask的使用

4、Future使用场景:
  实际工作中,可能需要统计各种类型的报表呈现结果,可能一个大的报表需要依赖很多小的模块的运算结果,一个线程做可能又比较慢,就可以拆分成N多个小线程,然后将其结果合并起来作为大的报表呈现结果;接下来的Fork/Join就是基于Future实现的;
5、什么是Fork/Join框架:
  是一个用于并行执行任务的框架;是一个把大任务分割成若干个小任务执行,最终汇总每个小任务结果后得到大任务结果的框架;
6、Fork/Join的JDK里面的家族:
7、Fork/Join框架的实现原理:
8、异常处理机制和办法:
9、Fork/Join模式优缺点及其实际应用场景:
  优点:对于符合Fork/Join模式的应用,软件开发人员不再需要处理各种并行相关事务,例如同步、通信等,以难以调试而闻名的死锁和data race等错误也就不会出现,提升了思考问题的层次;并行分发策略,仅仅关注如何划分任务和组合中间结果,将剩下的事情丢给Fork/Join框架完成即可;
  缺点:如果拆分的对象过多时,小心一下子把内存撑满;等待线程的CPU资源释放了,但是线程对象等待时不会被垃圾机制回收;
  使用场景:对于树形结构类型的数据的处理和遍历非常合适;

 1 /**
 2  * Fork/Join框架的使用:计算 1+2+3+4+5 的结果
 3  */
 4 package thread06;
 5
 6 import java.util.concurrent.ExecutionException;
 7 import java.util.concurrent.ForkJoinPool;
 8 import java.util.concurrent.Future;
 9 import java.util.concurrent.RecursiveTask;
10
11 public class ForkJoinTest01
12 {
13     public static void main(String[] args) throws InterruptedException, ExecutionException
14     {
15         ForkJoinPool pool = new ForkJoinPool();
16
17         CountTask task1 = new CountTask(1, 5);
18         Future<Integer> future = pool.submit(task1);
19         System.out.println("1-5最终相加的结果为:" + future.get());
20
21         CountTask task2 = new CountTask(1, 100);
22         Future<Integer> future2 = pool.submit(task2);
23         System.out.println("1-100最终相加的结果为:" + future2.get());
24     }
25 }
26
27 class CountTask extends RecursiveTask<Integer>
28 {
29     private static final long serialVersionUID = 1L;
30
31     private static int splitSize = 2;
32     private int start;
33     private int end;
34
35     public CountTask(int start, int end)
36     {
37         this.start = start;
38         this.end = end;
39     }
40
41     @Override
42     protected Integer compute()
43     {
44         int sum = 0;
45
46         // 如果任务已经不需要再拆分了,就开始计算
47         boolean canCompute = (end - start) <= splitSize;
48         if(canCompute)
49         {
50             // 如果是 1+2+3 也是走这个分支
51             for(int i=start;i<=end;i++)
52             {
53                 sum = sum + i;
54             }
55         }
56         else
57         {
58             // 拆分成两个子任务
59             int middle = (start + end) / 2;
60             CountTask firstTask = new CountTask(start, middle);
61             CountTask secondTask = new CountTask(middle+1, end);
62
63             // 子任务在调用fork方法时,又会进入compute方法,看看当前子任务是否需要继续分割成孙任务,
64             // 如果不需要继续分割,则执行当前子任务并返回结果
65             firstTask.fork();  // 开始计算
66             secondTask.fork();
67
68             // 获得第一个子任务的结果,得不到结果,此线程不会往下面执行
69             int firstResult = firstTask.join();
70             int secondResult = secondTask.join();
71
72             // 合并两个儿子的执行结果
73             sum = firstResult + secondResult;
74         }
75
76         return sum;
77     }
78
79 }

Fork/Join框架的使用:计算 1+2+3+4+5 的结果

原文地址:https://www.cnblogs.com/kehuaihan/p/8460301.html

时间: 2024-12-08 14:59:29

Java并发编程从入门到精通 - 第7章:Fork/Join框架的相关文章

Java并发编程从入门到精通 - 第6章:线程池

1.什么是线程池(为什么使用线程池):2.Executor框架介绍:  Java 5中引入的,其内部使用了线程池机制,在java.util.cocurrent 包下,通过该框架来控制线程的启动.执行和关闭(使用该框架来创建线程池),可以简化并发编程的操作:  Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等:  ExecutorService接口继承自Executor接口,

Java并发编程从入门到精通 - 第2章:认识Thread

线程实现的三种方法:1.三种实现方式的简记: 继承Thread类,重写run()方法: 实现Runnable接口,重写run()方法,子类创建对象并作为Thread类的构造器参数: 实现Callable接口,重写call()方法,子类创建对象并作为FutureTask类的构造器参数,FutureTask类创建对象并作为Thread类的构造器参数:2.三种实现方法的比较: 继承Thread类:因为是单继承,所以扩展性不好: 实现Runnable接口:接口可以多重实现:并且还可以再继承一个类:扩展性

Java并发编程从入门到精通 - 第3章:Thread安全

Java内存模型与多线程: 线程不安全与线程安全: 线程安全问题阐述:  多条语句操作多个线程共享的资源时,一个线程只执行了部分语句,还没执行完,另一个线程又进来操作共享数据(执行语句),导致共享数据最终结果出现误差:所以就是看一个线程能否每次在没有其他线程进入的情况下操作完包含共享资源的语句块,如果能就没有安全问题,不能就有安全问题: 如何模拟多线程的安全问题:  用Thread.sleep()方法模拟: 放在哪:放在多线程操作共享数据的语句块之间(使正在运行的线程休息一会,让其他线程执行,就

Java并发编程从入门到精通 - 第5章:多线程之间的交互:线程阀

详述: 线程阀是一种线程与线程之间相互制约和交互的机制: 作用:http://wsmajunfeng.iteye.com/blog/1629354阻塞队列BlockingQueue:数组阻塞队列ArrayBlockingQueue:链表阻塞队列LinkedBlockingQueue:优先级阻塞队列PriorityBlockingQueue:延时队列DelayQueue:同步队列SynchronousQueue:链表双向阻塞队列LinkedBlockingDeque:链表传输队列LinkedTra

Java并发编程从入门到精通-总纲

总纲: Thread; Thread安全; 线程安全的集合类; 多线程之间交互:线程阀; 线程池; Fork/Join; 第2章:认识Thread: 线程实现的三种方法; Thread里面的属性和方法; 线程的中断机制; 线程的生命周期; 守护线程; 线程组; 当前线程的副本:ThreadLocal; 线程异常的处理; 第3章:Thread安全: Java内存模型与多线程: 线程不安全和线程安全: 隐式锁synchronized: 显式锁Lock和ReentrantLock: 显式锁ReadWr

Java并发编程从入门到精通 张振华.Jack --我的书

[当当.京东.天猫.亚马逊.新华书店等均有销售] 目 录 第一部分:线程并发基础 第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以

Java并发编程从入门到精通 张振华.Jack --【吐血推荐、热销书籍】

[当当.京东.天猫.亚马逊.新华书店等均有销售]目 录 第一部分:线程并发基础 第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以采

Java网络编程从入门到精通(4):DNS缓存

在通过DNS查找域名的过程中,可能会经过多台中间DNS服务器才能找到指定的域名,因此,在DNS服务器上查找域名是非常昂贵的操作.在Java中为了缓解这个问题,提供了DNS缓存.当InetAddress类第一次使用某个域名(如www.csdn.net)创建InetAddress对象后,JVM就会将这个域名和它从DNS上获得的信息(如IP地址)都保存在DNS缓存中.当下一次InetAddress类再使用这个域名时,就直接从DNS缓存里获得所需的信息,而无需再访问DNS服务器. DNS缓存在默认时将永

[Java 并发] Java并发编程实践 思维导图 - 第六章 任务执行

根据<Java并发编程实践>一书整理的思维导图.希望能够有所帮助. 第一部分: 第二部分: 第三部分: