这里的每个人似乎都认为实现Runnable是必经之路,我并不完全不同意它们,但是在我看来,还有一种扩展Thread的情况,实际上您已经在代码中进行了演示。
如果实现Runnable,则实现Runnable的类无法控制线程名称,它是可以设置线程名称的调用代码,如下所示:
new Thread(myRunnable,"WhateverNameiFeelLike");
但是如果扩展Thread,则可以在类本身中进行管理(就像在示例中将线程命名为“ ThreadB”一样)。在这种情况下,您:
A)可能会给它起一个更有用的名称以用于调试
B)强制将该名称用于该类的所有实例(除非您忽略它是线程的事实并对其进行处理,就好像它是Runnable一样,但是无论如何我们都在这里讨论约定,可以忽略我的那种可能性)。
例如,您甚至可能会跟踪其创建的堆栈跟踪,并将其用作线程名。这可能看起来很奇怪,但是取决于代码的结构,对于调试目的可能非常有用。
这看起来似乎是一件小事,但是如果您有一个非常复杂的应用程序,其中包含许多线程,并且突然之间所有操作都已“停止”(由于死锁的原因或可能是由于网络协议中的缺陷所致,这种缺陷会更少很明显-或其他无尽的原因),然后从Java中获取所有线程都称为'Thread-1','Thread-2','Thread-3'的堆栈转储并不总是很有用(这取决于线程的使用方式)结构化的信息,以及是否可以仅通过堆栈跟踪有效地分辨出哪个-如果您使用的是多个都运行相同代码的线程组,则并非总是可能的。
话虽这么说,您当然也可以通过创建线程类的扩展名(通过将其名称设置为其创建调用的堆栈跟踪),然后将其与您的Runnable实现(而不是标准的Java Thread类)一起使用来以通用的方式进行操作(请参见下文),但除了堆栈跟踪信息外,可能还有更多特定于上下文的信息,这些信息将在线程名中用于调试(例如,对可能处理的许多队列或套接字之一的引用),在这种情况下,您可能更愿意专门针对这种情况扩展Thread,以便您可以让编译器强迫您(或其他使用您的库的)传递某些信息(例如,所讨论的队列/套接字)以用作名称。
这是一个以调用堆栈跟踪作为名称的通用线程的示例:
public class DebuggableThread extends Thread {
private static String getStackTrace(String name) {
Throwable t= new Throwable("DebuggableThread-"+name);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
t.printStackTrace(ps);
return os.toString();
}
public DebuggableThread(String name) {
super(getStackTrace(name));
}
public static void main(String[] args) throws Exception {
System.out.println(new Thread());
System.out.println(new DebuggableThread("MainTest"));
}
}
这是比较两个名称的输出示例:
Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
at DebuggableThread.getStackTrace(DebuggableThread.java:6)
at DebuggableThread.<init>(DebuggableThread.java:14)
at DebuggableThread.main(DebuggableThread.java:19)
,5,main]