Java中有2种类型的异常:
不能在 ForkJoinTask 的 compute() 方法中抛出受检查异常,因为此方法的实现不包含 throws 声明。 必须包含必要的代码来处理异常。另一方面,(它或此方法内使用的任意方法或对象)可以抛出不受检查的异常。 ForkJoinTask 和 ForkJoinPool 类的行为和您可能预期的不一样。程序不会结束执行,您也不会看到终端有关于异常的任何信息。 异常被简单地忽略,就像没有异常被抛出一样。 然而可以使用 ForkJoinTask 类的某些方法来获知一个任务是否抛出了异常以及抛出了什么类型的异常。
本节的示例代码在 com.elanzone.books.noteeg.chpt5.sect05 package中
任务类 : Task : extends RecursiveTask<Integer>
private static final long serialVersionUID = 1L; private int array[]; private int start, end; public Task(int[] array, int start, int end) { this.array = array; this.start = start; this.end = end; }
@Override protected Integer compute() { System.out.printf("Task: Start from %d to %d\n", start, end); if (end - start < 10) { if ((3 > start) && (3 < end)) { throw new RuntimeException("This task throws an Exception: Task from " + start + " to " + end); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } else { int mid = (end + start) / 2; Task task1 = new Task(array, start, mid); Task task2 = new Task(array, mid, end); invokeAll(task1, task2); } System.out.printf("Task: End form %d to %d\n", start, end); return 0; }
控制类 : Main
int array[] = new int[100]; Task task = new Task(array, 0, 100); ForkJoinPool pool = new ForkJoinPool(); pool.execute(task); pool.shutdown(); try { pool.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); }
if (task.isCompletedAbnormally()) { System.out.printf("Main: An exception has ocurred\n"); System.out.printf("Main: %s\n", task.getException()); } System.out.printf("Main: Result: %d", task.join());
Task 类处理一个数字数组。它检查它要处理的数字块是否有10或更多个元素。 如果是,则将数字块分为2个并对应创建2个新的 Task 对象来处理。 否则看数组的第4个元素是否在要处理的数字块中,如果在,则抛出 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 对象而您不期望有这样的行为时,要重视这点。