使用ThreadPoolExecutor,如何获取线程池中运行的线程的名称?

我在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()){ protected void beforeExecute(Thread t, Runnable r) { t.setName(deriveRunnableName(r)); } protected void afterExecute(Runnable r, Throwable t) { Thread.currentThread().setName(""); } protected  RunnableFuture newTaskFor(final Runnable runnable, V v) { return new FutureTask(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 RuntimeException * or Error that caused execution to terminate abruptly. * * 

Note: When actions are enclosed in tasks (such as * {@link FutureTask}) either explicitly or via methods such as * submit, these task objects catch and maintain * computational exceptions, and so they do not cause abrupt * termination, and the internal exceptions are not * passed to this method. * *

This implementation does nothing, but may be customized in * subclasses. Note: To properly nest multiple overridings, subclasses * should generally invoke super.afterExecute 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 } }); 

如果您愿意,也可以在完成后重置名称。