java - 如何在 TimeoutException 上获取未来的堆栈跟踪

lot*_*ons 5 java multithreading executorservice

在 java 中,我想确定在发生 TimeoutException 时填充未来结果的线程的当前堆栈。似乎 TimeoutException 提供的堆栈跟踪中的顶部条目仅指示调用 future.get() 的位置,而不是后台线程的状态。例如:

ExecutorService executor = Executors.newSingleThreadExecutor();

Future<String> future = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(10000);
        return "";
    }
});

try {
    future.get(1, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
    e.printStackTrace();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我发现最上面的条目是 future.get(1, TimeUnit.MILLISECONDS) 条目,而不是 Thread.sleep(10000)。我希望堆栈跟踪指示 Thread.sleep(10000) 因为这是当前正在执行的。有没有一种优雅的方式来做到这一点?

我发现,如果存在实际执行问题,那么 ExecutionException.printStackTrace() 将指示问题在后台线程中发生的位置。

Sol*_*low 7

如果你有一个引用,t,线程正在运行任务,你可以调用t.getStackTrace(); 但是标准库 ExecutorService 实现不会告诉您哪个线程正在运行任务。

您可以让任务本身记录正在运行它的线程:

class MyTask implements Callable<String> {
    private volatile Thread executorThread;

    @Override
    String call() {
        executorThread = Thread.currentThread(); // not getCurrentThread()
        Thread.sleep(10000);
        return "";
    }

    Thread getExecutorThread() {
        return executorThread;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,当您的主线程超时时,它可以调用 myTask.getExecutorThread().getStackTrace();

...
MyTask myTask = new MyTask();
Future<String> future = executor.submit(myTask);
...
} catch (InterruptedException | ExecutionException e) {
    StackTraceElement[] stack = myTask.getExecutorThread().getStackTrace();
    for (StackTraceElement element : stack) {
        ...print it...
    }
}
Run Code Online (Sandbox Code Playgroud)