有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

安卓javaexeutor框架的异常处理

我编写了一个安卓应用程序,用于杀死在后台线程中执行的后台运行进程。我使用下面的类来创建我自己的简单线程框架

public final class ThreadPool {
    private static final ExecutorService sES = Executors.newCachedThreadPool();

    public static Future<?> runNow(Runnable task) {
        return sES.submit(task);
    }
}

然而,出现了一个严重的问题。也就是说,异常(未检查的异常)将被Executor框架公平地消耗。所以我不知道为什么ActivityManager.killBackgroundProcesses()方法不起作用。在花了2到3个小时并在几乎每个方法调用点写了一些日志之后,我发现这个方法需要安卓。准许KILL_BACKGROUND_PROCESSES权限,否则,它将抛出一个未检查异常的SecurityException。关键是这个异常是由Excecutor框架使用的,所以我在logcat上看不到任何异常信息,并且应用程序根本没有崩溃,运行异常。 当然,一开始我不知道,所以我花了很多时间来找出原因,主要取决于两篇帖子: Handling exceptions from Java ExecutorService tasksCatching thread exceptions from Java ExecutorService

因此,我将my ThreadPool类更改为:

public final class ThreadPool {
    private static final ExecutorService sES = Executors.newCachedThreadPool();

    /*
     * submit(Runnable) and execute(Runnable) method has 
     * big difference. Especially in Exception handling!!!
     * You have to pay attention.
     */
    public static Future<?> submitNow(Runnable task) {
        return sES.submit(task);
    }

    public static void executeNow(Runnable task) {
        sES.execute(task);
    }
}

但我仍然有以下问题:

  1. 如果使用了submit(Runnable命令)方法,为什么Sun/Oracle决定使用异常而不是将异常传输给用户来处理
  2. 如果我坚持使用submit()方法,如何根据自己的需要更改此行为以处理未检查的异常

我的疑问是:

  1. 如果使用submit(Runnable命令)方法,我知道异常结果可以通过Future.get()方法获得。但是,如果我们用未来。get()方法来判断是否发生异常,位于中的线程未来对象将是block。我想,在大多数情况下,这不是我们所期望的
  2. 我还学习了Executor.execute()方法处理异常,就像处理公共线程一样。开始()。但是没有返回值。因此,任务在任何时候都无法关闭。离开活动时,用户无法通过Future.cancel()方法关闭任何正在运行的线程

共 (3) 个答案

  1. # 1 楼答案

    在线程化环境中,未经检查的异常是臭名昭著的,并且可能会发生奇怪的行为,如线程死亡、无异常日志等

    一个好方法是将可运行对象包装在线程中。创建一个线程组并将该线程添加到线程组

    final ThreadGroup group = new ThreadGroup("<a name for the thread group>");
    
    public static Future<?> submitNow(Runnable task) {
       //Create a thread wrapping the runnable task and specify the thread group
       Thread t = new Thread(group,task);
       return sES.submit(task);
    }
    

    ThreadGroup类有其uncaughtException(Thread,Throwable)方法,如果线程遇到异常并且在代码中未被捕获,JVM会自动调用该方法。见http://developer.android.com/reference/java/lang/ThreadGroup.html

    您还可以通过创建自己的ThreadGroup对象并重写uncaughtException方法来更改行为:

    public class MyThreadGroup extends ThreadGroup {
       @Override
       public void uncaughtException(Thread t, Throwable e) {
          //do what you need to do to handle the exception
       }
    }
    

    或者,您可以将UncaughtExceptionHandler分配给当前线程

    public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
    
       @Override
       public void uncaughtException(Thread t, Throwable t2) { 
         //Implement
       }
    }
    
    Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
    

    或者设置默认的异常处理程序:

    Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
    
  2. # 2 楼答案

    1. 如果将来调用get(),如果基础操作(可调用)引发异常,则会得到ExecutionExceptionSee the docs

    2. 您无法更改此行为(从第1点开始)

    这种方式实现的原因如下:submit是一个非阻塞调用。作业将发布到执行器中,并在稍后执行。 只有当作业被执行时,你才知道它是否崩溃,所以只有当你试图访问作业的结果时,你才会得到异常

  3. # 3 楼答案

    最后,我找到了一个好的解决方案

    我们可以在构造函数中扩展线程并调用setUncaughtHandler(),如下所示

    public class MyThread1 extends Thread {
    
    public MyThread1(Runnable task) {
        super(task);
        setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("thread throws an uncaught exception at thread id: " + t.getId());
            }
        });
    }
    

    }

    然后定制一个ThreadFactory,如下所示

        public class MyThreadFactory1 implements ThreadFactory {
            @Override
            public Thread newThread(Runnable r) {
                return new MyThread1(r, "Peace");
            }
    }
    

    所以我们可以在执行器中调用工厂方法,如下所示

    ExecutorService es = Executors.newSingleThreadExecutor(new MyThreadFactory1());
    

    因此,我们可以检测线程中发生的未捕获异常