[英]With ThreadPoolExecutor, how to get the name of the thread running in the thread pool?
我在Java中使用ThreadPoolExecutor
來管理大量正在運行的線程。 我已經創建了自己的簡單ThreadFactory
因此我可以為線程提供更好的名稱。
問題是,首次創建線程池時,名稱在線程中設置,並且不與線程池實際運行的任務相關聯。 我理解這一點......我的Runnables和Callables - 盡管它們有名字 - 實際上是ThreadPoolExecutor
運行線程的一個抽象層次。
StackOverflow上還有一些關於為ThreadPoolExecutor
線程池創建名稱的問題。 (請參閱如何為可調用線程命名?以及如何在Java中命名線程池的線程 。)
我想知道的是:有沒有人有一個很好的解決方案來保持線程池線程的名稱與它實際運行的Runnable同步?
即如果我調用Thread.getCurrentThread().getName()
我希望它不返回頂級線程池的名稱,而是返回線程當前正在運行的Callable / Runnable的名稱。
由於這主要是為了調試和日志記錄的目的,我試圖避免一個解決方案,涉及我將新代碼放入可能提交給ThreadPoolExecutor的每個Runnable中 - 我只是將一些代碼放入ThreadFactory或包裝ThreadPoolExecutor本身,以便在一個地方完成更改。 如果不存在這樣的解決方案,我可能不會打擾,因為它不是關鍵任務。
開始編輯為了澄清,我知道我可以放一個Thread.currentThread().setName( "my runnable name" );
作為每個Runnable的run方法的第一行,但我試圖避免這樣做。 我在這里是一個完美主義者,我意識到這一點,所以如果人們想對這個問題發表評論並告訴我,我不會被冒犯。 結束編輯
我想,我的另一個問題是人們是否認為做這樣的事情是個壞主意。 我是否應該像這樣更新線程池名稱?
謝謝你的任何建議!
創建一個覆蓋beforeExecute方法的ThreadPoolExecutor。
private final ThreadPoolExecutor executor = new ThreadPoolExecutor (new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()){
protected void beforeExecute(Thread t, Runnable r) {
t.setName(deriveRunnableName(r));
}
protected void afterExecute(Runnable r, Throwable t) {
Thread.currentThread().setName("");
}
protected <V> RunnableFuture<V> newTaskFor(final Runnable runnable, V v) {
return new FutureTask<V>(runnable, v) {
public String toString() {
return runnable.toString();
}
};
};
}
不確定derveRunnableName()
究竟是如何工作的,也許是toString()
?
編輯:Thread.currentThread()實際上是在beforeExecute中設置的線程,它調用afterExecute。 您可以引用Thread.currentThread(),然后在afterExecute中設置名稱。 這在javadocs中有說明
/**
* Method invoked upon completion of execution of the given Runnable.
* This method is invoked by the thread that executed the task. If
* non-null, the Throwable is the uncaught <tt>RuntimeException</tt>
* or <tt>Error</tt> that caused execution to terminate abruptly.
*
* <p><b>Note:</b> When actions are enclosed in tasks (such as
* {@link FutureTask}) either explicitly or via methods such as
* <tt>submit</tt>, these task objects catch and maintain
* computational exceptions, and so they do not cause abrupt
* termination, and the internal exceptions are <em>not</em>
* passed to this method.
*
* <p>This implementation does nothing, but may be customized in
* subclasses. Note: To properly nest multiple overridings, subclasses
* should generally invoke <tt>super.afterExecute</tt> at the
* beginning of this method.
*
* @param r the runnable that has completed.
* @param t the exception that caused termination, or null if
* execution completed normally.
*/
protected void afterExecute(Runnable r, Throwable t) { }
編輯 TPE將Runnable包裝在FutureTask中,因此為了支持toString
方法,您可以覆蓋newTaskFor
並創建自己的包裝FutureTask。
所以,我有一個解決方案,既管理名稱,又管理名稱后的清理。 感謝Peter Lawrey和John Vint提出的建議。 由於這兩個建議都沒有完全解決我的問題,我想我會將此示例代碼作為單獨的答案發布。 如果這是一個糟糕的禮儀,我道歉 - 如果是這樣,請告訴我,我會調整。
在下面的代碼中,我決定保留原始ThreadPoolExecutor線程的名稱並附加Runnable名稱,然后在finally塊中刪除Runnable名稱以進行清理,但這可以很容易地改變。
正如John Vint建議的那樣,我更喜歡覆蓋beforeExecution
方法,然后覆蓋afterExecution
方法來清理,但是afterExecution
沒有線程的句柄。
public class RunnableNameThreadPoolExecutor extends ThreadPoolExecutor {
/* Constructors... */
@Override
public void execute(Runnable command) {
super.execute(new ManageNameRunnable(command));
}
private class ManageNameRunnable implements Runnable {
private final Runnable command;
ManageNameRunnable( Runnable command ) {
this.command = command;
}
public void run() {
String originalName = Thread.currentThread().getName();
try {
String runnableName = getRunnableName(command);
Thread.currentThread().setName(originalName+ ": " + runnableName);
command.run();
} finally {
Thread.currentThread().setName(originalName);
}
}
}
}
我的建議是嘗試
pool.execute(new Runnable() {
public void run() {
Thread.getCurrentThread().setName("My descriptive Runnable");
// do my descriptive Runnable
}
});
如果您願意,也可以在完成后重置名稱。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.