fork/join框架能够为ForkJoinPool类的工作线程抛出的异常设置处理器。当使用ForkJoinPool类时,需要理解任务和工作线程之间的区别。
为了使用fork/join框架,需要实现继承ForkJoinTask类、RecursiveAction或者RecursiveTask类。任务实现与框架同时执行的操作,这些操作在ForkJoinPool类中通过工作线程来执行。工作线程会执行各种任务,在ForkJoinPool类实现的工作窃取算法中,工作线程在执行的任务完成或等待另一个任务完成时查找新任务。
本节讲学习如何处理工作线程抛出的异常,需要实现两个附加元素,其工作描述如下所示:
- 第一个元素是ForkJoinWorkerThread类的继承类,实现了ForkJoinPool类的工作线程。本范例将实现抛出异常的基础子类。
- 第二个元素是创建自定义类型的工作线程的工厂。ForkJoinPool类使用工厂创建工作线程,需要实现一个类,此类实现ForkJoinWorkerThreadFactory接口,且在ForkJoinPool类的构造函数中使用此类的对象。创建的ForkJoinPool对象使用工厂创建工作线程。
实现过程
通过如下步骤实现范例:
-
首先实现自动以工作线程类,创建名为AlwaysThrowsExceptionWorkerThread的类,继承ForkJoinWorkerThread类:
public class AlwaysThrowsExceptionWorkerThread extends ForkJoinWorkerThread {
-
实现类构造函数,将ForkJoinPool类作为参数接收,调用其父类的构造函数:
protected AlwaysThrowsExceptionWorkerThread(ForkJoinPool pool) { super(pool); }
-
实现onStart()方法,这是ForkJoinWorkerThread类的方法,在工作线程开始运行时执行。此实现将在被调用时抛出RuntimeException异常。
protected void onStart() { super.onStart(); throw new RuntimeException("Exception from worker thread"); } }
-
现在实现创建工作线程的工厂。创建名为AlwaysThrowsExceptionWorkerThreadFactory的类,实现ForkJoinWorkerThreadFactory接口:
public class AlwaysThrowsExceptionWorkerThreadFactory implements ForkJoinWorkerThreadFactory {
-
实现newThread()方法,将ForkJoinPool对象作为参数接收并返回ForkJoinWorkerThread对象。创建AlwaysThrowsExceptionWorkerThread对象并返回:
@Override public ForkJoinWorkerThread newThread(ForkJoinPool pool) { return new AlwaysThrowsExceptionWorkerThread(pool); } }
-
实现类来管理工作线程抛出的异常,实现名为Handler的类,实现UncaughtExceptionHandler接口:
public class Handler implements UncaughtExceptionHandler {
-
实现uncaughtException()方法,将Thread对象和Throwable对象作为参数接收,且每次工作线程抛出异常时被ForkJoinPool类调用。输出信息到控制台,退出程序:
@Override public void uncaughtException(Thread t, Throwable e) { System.out.printf("Handler: Thread %s has thrown anException.\n",t.getName()); System.out.printf("%s\n",e); System.exit(-1); } }
-
现在实现执行在ForkJoinPool执行器中的任务,创建名为OneSecondLongTask的类,继承RecursiveAction类:
public class OneSecondLongTask extends RecursiveAction{
-
实现compute()方法,很简单的设置线程休眠1秒钟:
@Override protected void compute() { System.out.printf("Task: Starting.\n"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Task: Finish.\n"); } }
-
现在,实现本范例主类,创建名为Main的类,包含main()方法:
public class Main { public static void main(String[] args) {
-
创建新的OneSecondLongTask对象:
OneSecondLongTask task=new OneSecondLongTask();
-
创建新的Handler对象:
Handler handler = new Handler();
-
创建新的AlwaysThrowsExceptionWorkerThreadFactory类:
AlwaysThrowsExceptionWorkerThreadFactory factory=new AlwaysThrowsExceptionWorkerThreadFactory();
-
创建新的ForkJoinPool对象,传参数值2、工厂对象,处理器对象和false值:
ForkJoinPool pool=new ForkJoinPool(2,factory,handler,false);
-
使用execute()方法执行池中的任务:
pool.execute(task);
-
使用shutdown()方法关闭池:
pool.shutdown();
-
使用awaitTermination()方法等待任务结束:
try { pool.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); }
-
输出指明程序结束的信息到控制台:
System.out.printf("Task: Finish.\n"); } }
工作原理
本节实现了下列元素:
- **自定义线程类:**实现了AlwaysThrowsExceptionWorkerThread类,此类继承ForkJoinWorkerThread类且实现fork/join池中的工作线程。重写了onStart()方法,当工作线程开始运行时执行此方法,当被调用时抛出RuntimeException异常。
- **自定义线程工厂:**ForkJoinPool类使用工厂创建工作线程,当使用AlwaysThrowsExceptionWorkerThreadFactory工作线程创建ForkJoinPool对象时,我们已经实现创建对象的工厂。为了实现工作线程工厂,需要实现ForkJoinWorkerThreadFactory接口,此接口只有一个名为newThread()的方法,创建工作线程并且将其返回给ForkJoinPool类。
- **任务类:**工作线程执行发送到ForkJoinPool执行器的任务,当开始工作线程执行时,需要发送任务到ForkJoinPool执行器。任务休眠1秒钟,但是当AlwaysThrowsExceptionWorkerThread线程抛出异常时,它将永不被执行。
- **未捕获异常的处理器类 :**当工作线程抛出异常时,ForkJoinPool类检查是否已注册异常处理器,为此已经实现了Handler类。此处理器实现了UncaughtExceptionHandler接口,此接口只有一个名为uncaughtException()的方法,此方法将抛出异常的线程作为参数接收。
在主类中,把这些元素放置在一起,传递四个参数到ForkJoinPool类的构造函数:并行级别、活跃工作线程数、在ForkJoinPool类中使用的工作线程工厂、用于工作线程未捕获异常的处理器,以及异步模式。
下图显示本范例在控制台输出的执行信息:
当执行程序时,工作线程抛出RuntimeException异常,ForkJoinPool类将异常交给处理器,处理器输出消息到控制台并退出程序。任务并未开始执行。
扩展学习
可以测试本范例两个有趣的变体:
-
如果在Handler类中注释这行代码且运行,将看到控制台输出很多信息。ForkJoinPool类试图开启工作线程执行任务,但由于总是抛出异常所以无法启动,因此一次次的去尝试:
System.exit(-1);
-
如果为null值更改ForkJoinPool类构造函数的第三个参数(异常处理器),就会发生类似情况。这种情况下,将看到JVM如何输出异常到控制台。
-
注意当实现自定义工作线程时,可能会抛出异常。
更多关注
- 第五章“Fork/Join框架”中的“创建fork/join池”小节
- 第八章“定制并发类”中的“定制在fork/join框架中运行的任务”和“实现为fork/join框架生成自定义线程的ThreadFactory接口”小节