ForkJoinPool类中处理不受控异常

Java 9并发编程指南 目录

ForkJoinPool类中处理不受控异常

  • 实现过程
  • 工作原理
  • 扩展学习
  • 更多关注

fork/join框架能够为ForkJoinPool类的工作线程抛出的异常设置处理器。当使用ForkJoinPool类时,需要理解任务和工作线程之间的区别。

为了使用fork/join框架,需要实现继承ForkJoinTask类、RecursiveAction或者RecursiveTask类。任务实现与框架同时执行的操作,这些操作在ForkJoinPool类中通过工作线程来执行。工作线程会执行各种任务,在ForkJoinPool类实现的工作窃取算法中,工作线程在执行的任务完成或等待另一个任务完成时查找新任务。

本节讲学习如何处理工作线程抛出的异常,需要实现两个附加元素,其工作描述如下所示:

  • 第一个元素是ForkJoinWorkerThread类的继承类,实现了ForkJoinPool类的工作线程。本范例将实现抛出异常的基础子类。
  • 第二个元素是创建自定义类型的工作线程的工厂。ForkJoinPool类使用工厂创建工作线程,需要实现一个类,此类实现ForkJoinWorkerThreadFactory接口,且在ForkJoinPool类的构造函数中使用此类的对象。创建的ForkJoinPool对象使用工厂创建工作线程。

实现过程

通过如下步骤实现范例:

  1. 首先实现自动以工作线程类,创建名为AlwaysThrowsExceptionWorkerThread的类,继承ForkJoinWorkerThread类:

    public class AlwaysThrowsExceptionWorkerThread extends ForkJoinWorkerThread {
    
  2. 实现类构造函数,将ForkJoinPool类作为参数接收,调用其父类的构造函数:

    	protected AlwaysThrowsExceptionWorkerThread(ForkJoinPool pool) {
    		super(pool);
    	}
    
  3. 实现onStart()方法,这是ForkJoinWorkerThread类的方法,在工作线程开始运行时执行。此实现将在被调用时抛出RuntimeException异常。

    	protected void onStart() {
    		super.onStart();
    		throw new RuntimeException("Exception from worker thread");
    	}
    }
    
  4. 现在实现创建工作线程的工厂。创建名为AlwaysThrowsExceptionWorkerThreadFactory的类,实现ForkJoinWorkerThreadFactory接口:

    public class AlwaysThrowsExceptionWorkerThreadFactory implements ForkJoinWorkerThreadFactory {
    
  5. 实现newThread()方法,将ForkJoinPool对象作为参数接收并返回ForkJoinWorkerThread对象。创建AlwaysThrowsExceptionWorkerThread对象并返回:

    	@Override
    	public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
    		return new AlwaysThrowsExceptionWorkerThread(pool);
    	}
    }
    
  6. 实现类来管理工作线程抛出的异常,实现名为Handler的类,实现UncaughtExceptionHandler接口:

    public class Handler implements UncaughtExceptionHandler {
    
  7. 实现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);
    	}
    }
    
  8. 现在实现执行在ForkJoinPool执行器中的任务,创建名为OneSecondLongTask的类,继承RecursiveAction类:

    public class OneSecondLongTask extends RecursiveAction{
    
  9. 实现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");
    	}
    }
    
  10. 现在,实现本范例主类,创建名为Main的类,包含main()方法:

    public class Main {
    	public static void main(String[] args) {
    
  11. 创建新的OneSecondLongTask对象:

    		OneSecondLongTask task=new OneSecondLongTask();
    
  12. 创建新的Handler对象:

    		Handler handler = new Handler();
    
  13. 创建新的AlwaysThrowsExceptionWorkerThreadFactory类:

    		AlwaysThrowsExceptionWorkerThreadFactory factory=new AlwaysThrowsExceptionWorkerThreadFactory();
    
  14. 创建新的ForkJoinPool对象,传参数值2、工厂对象,处理器对象和false值:

    		ForkJoinPool pool=new ForkJoinPool(2,factory,handler,false);
    
  15. 使用execute()方法执行池中的任务:

    		pool.execute(task);
    
  16. 使用shutdown()方法关闭池:

    		pool.shutdown();
    
  17. 使用awaitTermination()方法等待任务结束:

    		try {
    			pool.awaitTermination(1, TimeUnit.DAYS);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
  18. 输出指明程序结束的信息到控制台:

    		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类中使用的工作线程工厂、用于工作线程未捕获异常的处理器,以及异步模式。

下图显示本范例在控制台输出的执行信息:

pics/10_01.jpg

当执行程序时,工作线程抛出RuntimeException异常,ForkJoinPool类将异常交给处理器,处理器输出消息到控制台并退出程序。任务并未开始执行。

扩展学习

可以测试本范例两个有趣的变体:

  • 如果在Handler类中注释这行代码且运行,将看到控制台输出很多信息。ForkJoinPool类试图开启工作线程执行任务,但由于总是抛出异常所以无法启动,因此一次次的去尝试:

    System.exit(-1);
    
  • 如果为null值更改ForkJoinPool类构造函数的第三个参数(异常处理器),就会发生类似情况。这种情况下,将看到JVM如何输出异常到控制台。

  • 注意当实现自定义工作线程时,可能会抛出异常。

更多关注

  • 第五章“Fork/Join框架”中的“创建fork/join池”小节
  • 第八章“定制并发类”中的“定制在fork/join框架中运行的任务”和“实现为fork/join框架生成自定义线程的ThreadFactory接口”小节

你可能感兴趣的:(Java,多线程)