我正在处理多线程项目,其中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();
}
}
似乎结果应该是我所期望的:抛出错误的线程终止,显示消息。事实证明,当使用Runnable
将ExecutorService
传递给submit(Runnable)
时,它会被RunnableFuture<Void>
包裹起来并且不会处理错误,并且除了直接调用execute(Runnable)
之外,我无法找到改变此行为的方法,而submit
由于某种原因没有表现出相同的行为。
是否有最佳实践&#34;为了这?如果我知道一个Thread可能会抛出一个错误,有没有办法{{1}}它到ExecutorService而不是吞下错误?
答案 0 :(得分: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();
}
您的堆栈跟踪将包含:
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)
答案 1 :(得分:1)
尝试处理从线程抛出的错误似乎是一个可疑的用例......
来自Java Doc:&#34;错误是Throwable的子类,表示合理的应用程序不应该尝试捕获的严重问题。大多数此类错误都是异常情况。 ThreadDeath错误,虽然&#34;正常&#34; condition,也是Error的子类,因为大多数应用程序都不应该尝试捕获它。&#34;
我需要处理错误的唯一一次是当我使用严重管理其异常的第三方时,抛出Error而不是RuntimeException。在这种特定情况下,您需要捕获错误(或Throwable)以避免意外的应用程序崩溃。
答案 2 :(得分:0)
Future
是一个对象,表示运行您的操作的结果,无论是现在还是将来某个时候。通常,人们会在所述未来调用get
方法以获得结果,特别是如果您将Callable
而不是Runnable
传递到ExecutorService
。< / p>
如果您的Runnable
/ Callable
引发异常,则会反映在Future
对象中。您可以通过调用Runnable
方法来测试get
是否成功运行。如果它是一个干净的运行,你将得到(在这种情况下)null
。如果抛出异常,则get
将抛出ExecutionException
,并将该异常标记为其原因。
把它想象成一个音乐场地的外套。如果他们丢了你的外套,他们可能不会告诉你,直到你拿到你的外套要求你的外套。 Future
符合您的机票目的。