Fork/Join框架(五)在任务中抛出异常

声明:本文是《 Java 7 Concurrency Cookbook》的第五章,作者: Javier Fernández González 译者:许巧辉 校对:方腾飞

在任务中抛出异常

在Java中有两种异常:

  • 已检查异常(Checked exceptions):这些异常必须在一个方法的throws从句中指定或在内部捕捉它们。比如:IOException或ClassNotFoundException。
  • 未检查异常(Unchecked exceptions):这些异常不必指定或捕捉。比如:NumberFormatException。

在ForkJoinTask类的compute()方法中,你不能抛出任何已检查异常,因为在这个方法的实现中,它没有包含任何抛出(异常)声明。你必须包含必要的代码来处理异常。但是,你可以抛出(或者它可以被任何方法或使用内部方法的对象抛出)一个未检查异常。ForkJoinTask和ForkJoinPool类的行为与你可能的期望不同。程序不会结束执行,并且你将不会在控制台看到任何关于异常的信息。它只是被吞没,好像它没抛出(异常)。你可以使用ForkJoinTask类的一些方法,得知一个任务是否抛出异常及其异常种类。在这个指南中,你将学习如何获取这些信息。

准备工作

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现这个例子:

1.创建Task类。指定它实现RecursiveTask类,并参数化为Integer类型。

1 public class Task extends RecursiveTask<Integer> {

2.声明一个私有的、int类型数组的属性array。它将模拟在这个指南中,你将要处理的数据的数组。

1 private int array[];

3.声明两个私有的、int类型的属性start和end。这些属性将决定这个任务要处理的数组的元素。

1 private int start, end;

4.实现这个类的构造器,初始化它的属性。

1 public Task(int array[], int start, int end){
2 this.array=array;
3 this.start=start;
4 this.end=end;
5 }

5.实现这个任务的compute()方法。正如你使用Integer类型参数化RecursiveTask类一样,这个方法将返回一个Integer对象。首先,将start和end值写入到控制台。

1 @Override
2 protected Integer compute() {
3 System.out.printf("Task: Start from %d to %d\n",start,end);

6.如果这个任务将要处理的,由start和end属性决定的元素块的大小小于10,检查数组的第4位置(索引号3)的元素是否在那个块中。如果是这种情况,抛出一个RuntimeException异常。然后,令这个任务睡眠1秒。

01 if (end-start<10) {
02 if ((3>start)&&(3<end)){
03 throw new RuntimeException("This task throws an"+
04 "Exception: Task from "+start+" to "+end);
05 }
06 try {
07 TimeUnit.SECONDS.sleep(1);
08 } catch (InterruptedException e) {
09 e.printStackTrace();
10 }

7.否则(这个任务将要处理的元素块的大小等于或大于10),将这个元素块分成两个部分,创建2个Task对象来处理这些块,在池中使用invokeAll()方法执行它们。

1 } else {
2 int mid=(end+start)/2;
3 Task task1=new Task(array,start,mid);
4 Task task2=new Task(array,mid,end);
5 invokeAll(task1, task2);
6 }

8.写入一条信息(start和end属性值)到控制台,表明任务的结束。

1 System.out.printf("Task: End form %d to %d\n",start,end);

9.返回数字0作为任务的结果。

1 return 0;

10.实现这个例子的主类,通过创建Main类,并实现main()方法。

1 public class Main {
2 public static void main(String[] args) {

11.创建一个大小为100的整数数组。

1 int array[]=new int[100];

12.创建一个Task对象来处理这个数组。

1 Task task=new Task(array,0,100);

13.使用默认构造器创建一个ForkJoinPool对象。

1 ForkJoinPool pool=new ForkJoinPool();

14.在池中使用execute()方法执行这个任务。

1 pool.execute(task);

15.使用shutdown()方法关闭ForkJoinPool类。

1 pool.shutdown();

16.使用awaitTermination()方法等待任务的结束。如果你想要等待任务的结束,无论它花多长时间结束,将值1和TimeUnit.DAYS作为参数传给这个方法。

1 try {
2 pool.awaitTermination(1, TimeUnit.DAYS);
3 } catch (InterruptedException e) {
4 e.printStackTrace();
5 }

17.使用isCompletedAbnormally()方法,检查这个任务或它的子任务是否已经抛出异常。在这种情况下,将抛出的异常写入到控制台。使用ForkJoinTask类的getException()方法获取那个异常。

1 if (task.isCompletedAbnormally()) {
2 System.out.printf("Main: An exception has ocurred\n");
3 System.out.printf("Main: %s\n",task.getException());
4 }
5 System.out.printf("Main: Result: %d",task.join());

它是如何工作的…

在这个指南中,你已经实现Task类来处理一个数字数组。它检查要处理的数字块是否是10个或更多的元素。在这种情况下,它将数字块分成两块,并创建两个新的Task对象来处理这些块。否则,他查找数组中的第4个位置的元素(索引号3)。如果这个元素在任务要处理的块中,它抛出一个RuntimeException异常。

当你执行这个程序,异常是抛出了,但程序并没有停止。在Main类中,你已经使用发起任务调用ForkJoinTask类的isCompletedAbnormally()方法。如果任务或它的子任务抛出异常,这个方法返回true。你同时使用了同样对象的getException()方法来获取已抛出的Exception对象。

当你在一个任务中抛出一个未检查异常时,它也影响到它的父任务(把它提交到ForkJoinPool类的任务)和父任务的父任务,以此类推。如果你修订程序的所有输出,你将会看到一些任务结束没有输出信息。这些任务的开始信息如下:

Task: Starting form 0 to 100
Task: Starting form 0 to 50
Task: Starting form 0 to 25
Task: Starting form 0 to 12
Task: Starting form 0 to 6

这些任务是那些及其父任务抛出异常的任务。它们全部异常地完成。考虑到这一点,当你使用ForkJoinPool和ForkJoinTask对象开发一个程序,当你不想这种行为时,可以抛出异常。

以下截图显示了这个例子执行的一部分:



不止这些…

你可以获取与这个例子相同的结果,如果不是抛出异常,你可以使用ForkJoinTask类的completeExceptionally()方法。代码如下:

1 Exception e=new Exception("This task throws an Exception: "+ "Task
2 from "+start+" to "+end);
3 completeExceptionally(e);

参见

时间: 2024-10-01 06:01:46

Fork/Join框架(五)在任务中抛出异常的相关文章

《Java 7并发编程实战手册》第五章Fork/Join框架

感谢人民邮电大学授权并发网发布此书样章,新书已上市,购买请进当当网 本章内容包含: 创建Fork/Join线程池 合并任务的结果 异步运行任务 在任务中抛出异常 取消任务 5.1 简介 通常,使用Java来开发一个简单的并发应用程序时,会创建一些Runnable对象,然后创建对应的Thread 对象来控制程序中这些线程的创建.执行以及线程的状态.自从Java 5开始引入了Executor和ExecutorService接口以及实现这两个接口的类(比如ThreadPoolExecutor)之后,使

定制并发类(八)自定义在 Fork/Join 框架中运行的任务

声明:本文是< Java 7 Concurrency Cookbook>的第七章, 作者: Javier Fernández González 译者:郑玉婷 自定义在 Fork/Join 框架中运行的任务 执行者框架分开了任务的创建和运行.这样,你只要实现 Runnable 对象来使用 Executor 对象.你可以发送 Runnable 任务给执行者,然后它会创建,管理,并终结必要的线程来执行这些任务. Java 7 在 Fork/Join 框架中提供了特殊的执行者.这个框架是设计用来解决那

利用Fork/Join框架来统计某个字符串在某个文件夹的文件中出现的次数

需求:利用Fork/Join框架来统计某个字符串在某个文件夹的文件中出现的次数.主要用到的技术点:JDK8的Stream.Lambda表达式.Fork/Join框架. 如果对于Fork/Join不了解的话可以参考这里:http://www.infoq.com/cn/articles/fork-join-introduction.http://ifeve.com/fork-join-1/.http://www.iteye.com/topic/643724.主要代码如下: package com.z

聊聊并发(八)——Fork/Join框架介绍

本文首发于InfoQ 1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. 我们再通过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果.比如计算1+2+..+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和,最终汇

Fork/Join框架(一)引言

在这个章节中,我们将覆盖: 创建一个Fork/Join池 加入任务的结果 异步运行任务 任务抛出异常 取消任务 引言 通常,当你实现一个简单的并发应用程序,你实现一些Runnable对象和相应的 Thread对象.在你的程序中,你控制这些线程的创建.执行和状态.Java 5引入了Executor和ExecutorService接口及其实现类进行了改进(比如:ThreadPoolExecutor类). 执行者框架将任务的创建与执行分离.有了它,你只要实现Runnable对象和使用Executor对

从命令式编程到Fork/Join再到Java 8中的并行Streams

Java 8带来了很多可以使编码更简洁的特性.例如,像下面的代码: Collections.sort(transactions, new Comparator<Transaction>(){ public int compare(Transaction t1, Transaction t2){ return t1.getValue().compareTo(t2.getValue()); } }); 可以用替换为如下更为紧凑的代码,功能相同,但是读上去与问题语句本身更接近了: transacti

Java Fork/Join框架_java

Fork/Join框架是ExecutorService接口的一个实现,通过它我们可以实现多进程.Fork/Join可以用来将一个大任务递归的拆分为多个小任务,目标是充分利用所有的资源尽可能增强应用的性能. 和任何ExecutorService接口的实现一样,Fork/Join也会使用线程池来分布式的管理工作线程.Fork/Join框架的独特之处在于它使用了work-stealing(工作窃取)算法.通过这个算法,工作线程在无事可做时可以窃取其它正在繁忙的线程的任务来执行. Fork/Join框架

Fork/Join框架(三)加入任务的结果

加入任务的结果 Fork/Join框架提供了执行返回一个结果的任务的能力.这些任务的类型是实现了RecursiveTask类.这个类继承了ForkJoinTask类和实现了执行者框架提供的Future接口. 在任务中,你必须使用Java API方法推荐的结构: 1 If (problem size < size){ 2 tasks=Divide(task); 3 execute(tasks); 4 groupResults() 5 return result; 6 } else { 7 reso

定制并发类(七)实现ThreadFactory接口生成自定义的线程给Fork/Join框架

声明:本文是< Java 7 Concurrency Cookbook>的第七章,作者: Javier Fernández González     译者:许巧辉 实现ThreadFactory接口生成自定义的线程给Fork/Join框架 Fork/Join框架是Java7中最有趣的特征之一.它是Executor和ExecutorService接口的一个实现,允许你执行Callable和Runnable任务而不用管理这些执行线程. 这个执行者面向执行能被拆分成更小部分的任务.主要组件如下: 一