What is the proper way to keep track of the original stack trace in a newly created Thread?
In the client program on which I work, we dispatch server calls to different Threads, to not lock the UI (equivalent of a SwingWorker).
This is made with a model class inheriting an abstract class containing the "update" method, which is preparing the new thread, and executing the code from an abstract method in this newly created Thread (plus other tweaks)
It works correctly, but my issue is that when debugging (or logging), it is hard to keep track of which method exactly called the "update" method, since the stack trace ends with the creation of the new Thread.
What would be the proper way to keep track of the stack trace which led to call this new Thread? Ideally, in a way which would show in the debugger's stack navigator (from Eclipse in this case; the idea is to navigate easily in 开发者_运维问答the initial context).
Typically stack traces don't cross threads so this is going to be tricky. However, in the constructor of your Worker class you could access the current thread with...
Thread current = Thread.currentThread
You can then get the current stack trace of that thread by calling...
StackTraceElement[] currentStack = current.getStackTrace();
You can then store that in an instance variable for your worker and view that from your debugger. This has to be done before the control passes into the new thread, that's why I suggested doing it in the constructor. However, any method that gets called before the start()
method of the new thread will be fine.
A good way to store a stacktrace away efficiently is to simply construct an exception. Later, if you want to inspect the stacktrace call exception.getStackTrace()
which will do the slow work of resolving the stack frames to methods.
So, you could create a new Exception
on the construction of your worker thread, or pass it to the worker thread. Note, you'll have to get eclipse to evaluate exception.getStackTrace(), because the exception object won't have the details before you do.
public abstract class Worker {
protected abstract Object doTheWork();
public Future<Object> update() {
Exception stack = new Exception();
Callable<Object> job = new WhateverYourCallableIs(stack);
return submitJob(job);
}
}
Incidentally, you should probably use an ExecutorService to manage the lifecycle of your threads.
Edit I'm suggesting this way because it will have a minimal impact on performance in the usual case where you don't want to see the stack trace.
You say you do "like SwingWorker". In this case, why not using an ExecutorService ? Java concurrent framework is pretty well made, and would allow you to avoid the classic pitfalls of threading, amongst them your question.
I just had the same problem - multiple points in the system were sending a mail through the same logic wrapped in an ExecutorService
.
Just like @daveb I create an empty Exception
and pass it into the Runnable
:
Executors.newSingleThreadExecutor().submit(new Mailer(..., new Exception()));
Now inside of Mailer
I have the Exception
instance to use in logging expressions:
public Mailer(..., final Exception originalStackKeeper) {
...
this.originalStackKeeper = originalStackKeeper;
}
...
LOG.error("There was an error while sending mail, originating here: ",
originalStackKeeper);
But even better: whenever I catch an exception in Mailer
, I can do this:
} catch (final SomeException e) {
LOG.error("There was an error while sending mail.",
originalStackKeeper.initCause(e));
}
So I tell the original Exception
: let's use your stack: here is the cause why we are using you, and here we are logging you. It seems to me that this is not really a hack, but rather a clean way to use the "cause" mechanism in Java exceptions.
精彩评论