【Thread】多线程的异常处理?

线程中处理异常是个头疼的问题,在异步的代码中,如何将异常捕获

捕获异常后,将异常反馈给开发者,或用户。一般来说,反馈给开发者,多数方式在是日志中打印异常日志;而反馈给用户,多数是在界面上友好提示(比如弹提示框)。

 

> UncaughtExceptionHandler方式处理异常

在线程当中遇到异常,需要捕捉并打印日志,我们可以考虑使用UncaughtExceptionHandler的回调

以下是一个简单的测试例子。

 

有一个线程,方法体中会抛出异常

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);
        }
    }

}

 

日志如下,可见:

  1. 如果正确设置了异常处理器,异常将在异常处理器打印
  2. 无论有无设置异常处理器,异常都不会被直接的catch块捕捉和打印
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;
    }
    
}
View Code

 

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;
    }

}
View Code

 

日志:

看到“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)
View Code

 

Runnable的线程也是可以的:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " run.");
        throw new RuntimeException("模拟的一个异常");
    }

    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        
        try {
            Future<?> f = es.submit(new MyThread());
            System.out.println(f.get());
        } catch (Throwable t) {
            System.out.println("捕获异常 : " + t.getMessage());
        }
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值