Java如何处理多线程中的错误?

ndm*_*m13 3 java error-handling multithreading

我正在研究一个多线程项目,它有一个Thread可以抛出Error(而不是Exception)的情况.没有找到关于如何在多线程中处理错误的任何可靠信息,我决定做一些测试,发现结果可能不一致.

这是我的测试代码,以及评论结果.

public class MultiThreadError {
    public static class ErrorThrowingRunnable implements Runnable{
        private final boolean throwsError;
        public ErrorThrowingRunnable(boolean throwsError){
            this.throwsError = throwsError;
        }

        @Override
        public void run() {
            try {
                // Wait between .5 and 1.5 seconds
                Thread.sleep(500 + new Random().nextInt(1000));
            } catch (InterruptedException ex) {}
            if(throwsError){
                throw new Error(Thread.currentThread().getName());
            }else{
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    public static void regularThreadPool(){
        // Crashes individual thread; swallows error
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(false));
        threadPool.submit(new ErrorThrowingRunnable(true));
        threadPool.shutdown();
    }

    public static void onDemandThreads(){
        // Crashes individual thread; displays error
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(false)).start();
        new Thread(new ErrorThrowingRunnable(true)).start();
    }

    public static void onDemandThreadPool(){
        // Same as onDemandThreads()
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(true));
        threadPool.shutdown();
    }

    public static void tooSmallThreadPool(){
        // When an error is thrown, apparently the thread that threw
        // the error is not reused, reducing the pool size
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.execute(new ErrorThrowingRunnable(true));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.execute(new ErrorThrowingRunnable(false));
        threadPool.shutdown();
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎结果应该是我所期望的:抛出错误的线程终止,显示消息.事实证明,当a Runnable被传递给一个ExecutorService使用时submit(Runnable),它被包裹在一个RunnableFuture<Void>不处理错误的东西中,除了直接调用之外我找不到改变这种行为的方法execute(Runnable),由于某种原因它没有表现出相同的行为.

对此有"最佳实践"吗?如果我知道一个线程可能会抛出一个错误,有没有办法submit让它到ExecutorService而不是吞下错误?

MGo*_*gon 5

是的,将您的任务提交给ExecutorService并检查返回的结果Future.

使用时:

ExecutorService es = Executors.newFixedThreadPool(1);

Future<?> result = es.submit(new Runnable() {
    @Override
    public void run() {
        throw new Error("sample error");
    }
});

try {
    result.get();
} catch (ExecutionException e) {
    e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

您的堆栈跟踪将包含:

java.util.concurrent.ExecutionException: java.lang.Error: sample error
    at java.util.concurrent.FutureTask.report(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at jjj.b.B.main(B.java:23)
Caused by: java.lang.Error: sample error
    at jjj.b.B$1.call(B.java:18)
    at jjj.b.B$1.call(B.java:1)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Run Code Online (Sandbox Code Playgroud)