线程中处理异常是个头疼的问题,在异步的代码中,如何将异常捕获。
捕获异常后,将异常反馈给开发者,或用户。反馈给开发者,多数方式在是日志中打印异常信息;而反馈给用户,多数是在界面上友好提示。
以下是一个简单的测试例子。
有一个线程,方法体中会抛出异常
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; public class ExceptionThread implements Runnable { @Override public void run() { throw new NullPointerException("故意抛出的异常。"); } }
自定义的异常处理器,这里所做的操作仅是在控制台打印异常,告诉开发者(并非用户哦)有这么个异常发生
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.lang.Thread.UncaughtExceptionHandler; public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable throwable) { System.out.println("Print exception @ MyUncaughtExceptionHandler -> " + throwable); } }
为设置方便,通常可以有一个线程工厂,已帮忙批量以通用的参数设置产生线程。见调用类可见,有触发方法使用到此工厂,也有直接实例化线程的。
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.util.concurrent.ThreadFactory; public class HandlerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); return t; } }
触发类,这里介绍两种设置方式(一直接设置;一通过工厂设置),还有一种错误的设置方式,以供借镜。
package com.nicchagil.study.thread.thinking.No15线程异常的捕捉; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Call { public static void main(String[] args) { /* Work */ callByWay1(); /* Work */ callByWay2(); /* Un-work */ callByWay3(); } /** * 原始的调用方式,在Thread对象设置异常处理器 */ public static void callByWay1() { try { Thread t = new Thread(new ExceptionThread()); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); t.start(); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } /** * 使用工厂设置异常处理器的调用方式 */ public static void callByWay2() { try { ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory()); es.execute(new ExceptionThread()); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } /** * 实例化Thread后,传入ExecutorService调用。(此方法不行,原因见代码内注释) */ public static void callByWay3() { try { Thread t = new Thread(new ExceptionThread()); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); ExecutorService es = Executors.newCachedThreadPool(); /* 此方法不行,异常处理器虽在前面设置,但由于es.execute(t)的入参为Runnable,故异常处理器遗失 */ es.execute(t); } catch (Throwable t) { System.out.println("Print exception @ Call.java -> " + t); } } }
日志如下,可见:
Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。 Print exception @ MyUncaughtExceptionHandler -> java.lang.NullPointerException: 故意抛出的异常。 Exception in thread "pool-1-thread-1" java.lang.NullPointerException: 故意抛出的异常。 at com.nicchagil.study.thread.thinking.No15线程异常的捕捉.ExceptionThread.run(ExceptionThread.java:7) at java.lang.Thread.run(Thread.java:619) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
告诉开发者有异常发生,就不用说了,直接打印日志就OK。而告诉用户有异常发生,则需要在线程执行过程中捕获,捕获后传递给页面。
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; public class QueryDataThread implements Callable<List<Map<String, Object>>> { private Map<String, Object> param = null; public QueryDataThread(Map<String, Object> param) { super(); this.param = param; } public List<Map<String, Object>> call() throws Exception { /* 模拟查询DB后得到结果集合 */ System.out.println("param -> " + param); Map<String, Object> map1 = new HashMap<String, Object>(); map1.put("id", "1001"); map1.put("name", "nick huang"); Map<String, Object> map2 = new HashMap<String, Object>(); map2.put("id", "1002"); map2.put("name", "darren lin"); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); list.add(map1); list.add(map2); /* 设置短暂的睡眠以便观察效果 */ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } /* 模拟异常 */ String s = ""; s.charAt(10); return list; } }
import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Call { public static void main(String[] args) { List<Map<String, Object>> list = null; try { list = query(); } catch (Exception e) { // TODO Auto-generated catch block System.out.println("Exception catched..."); e.printStackTrace(); } if (list != null && list.size() > 0) { for (Map<String, Object> map : list) { System.out.println("id : " + map.get("id") + ", name : " + map.get("name")); } } } /** * 模拟查询的业务方法 */ public static List<Map<String, Object>> query() throws InterruptedException, ExecutionException { ExecutorService es = Executors.newCachedThreadPool(); /* 模拟查询参数 */ Map<String, Object> param = new HashMap<String, Object>(); param.put("username", "derick"); Future<List<Map<String, Object>>> future = es.submit(new QueryDataThread(param)); List<Map<String, Object>> list = null; try { list = future.get(); } finally { es.shutdown(); } return list; } }
日志:
看到“Exception catched...”就知道异常被捕捉了,剩下的就看你如何传到JSP了,哈哈哈。
param -> {username=derick} Exception catched... java.util.concurrent.ExecutionException: java.lang.StringIndexOutOfBoundsException: String index out of range: 10 at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:188) at Call.query(Call.java:41) at Call.main(Call.java:14) Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 10 at java.lang.String.charAt(String.java:658) at QueryDataThread.call(QueryDataThread.java:42) at QueryDataThread.call(QueryDataThread.java:1) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)
原文:http://www.cnblogs.com/nick-huang/p/4768358.html