优先实现 Runnable 而非继承 Thread;因 Java 不支持多继承、Runnable 语义清晰且可复用,而继承 Thread 易误用 run()/start()、状态管理混乱、上下文不自动继承。

直接说结论:优先实现 Runnable,而不是继承 Thread;除非你明确需要重写 Thread 类本身的逻辑(极少见)。
为什么 Runnable 是更安全的选择
Java 不支持多继承,而 Thread 是一个具体类。如果你的业务类 already 继承了某个父类(比如 Service、AbstractProcessor),就无法再 extends Thread —— 这时 Runnable 是唯一可行路径。
-
Runnable是函数式接口,语义清晰:只表达“我要执行什么”,不耦合线程生命周期管理 - 线程资源可复用:
new Thread(runnable).start()和executor.execute(runnable)都能接收它,而Thread子类无法直接交给线程池 - 继承
Thread容易误用this:子类中调用start()或run()时,若没分清是启动新线程还是同步执行,会引发隐蔽 bug
Thread 子类的典型错误用法
常见错误是把 run() 当作普通方法调用:
class MyThread extends Thread {
public void run() { System.out.println("running"); }
}
// 错误:这不会启动线程,只是同步执行 run()
new MyThread().run();
// 正确:必须调用 start()
new MyThread().start();
另一个坑是重写了 start() 方法却忘了调用 super.start(),导致线程根本没创建。
立即学习“Java免费学习笔记(深入)”;
- 继承
Thread后,run()方法不再具备默认行为(空实现),但你不该依赖它做任何事 - 所有状态变量如果定义在
Thread子类里,容易被误认为“线程私有”,其实只是实例字段——多个实例之间并不共享,但和线程绑定关系弱,可读性差
Runnable 的正确写法与参数传递
不要写匿名内部类硬编码参数;用构造器传参更清晰、可测试:
class Task implements Runnable {
private final String name;
private final int delay;
Task(String name, int delay) {
this.name = name;
this.delay = delay;
}
public void run() {
try {
Thread.sleep(delay);
System.out.println(name + " done");
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 关键:恢复中断状态
}
}
}
// 使用
new Thread(new Task("job-1", 1000)).start();
- 务必处理
InterruptedException:不能吞掉,也不能只打印日志;推荐调用Thread.currentThread().interrupt() - 避免在
Runnable构造器里做耗时操作或阻塞,否则 new 实例就会卡住主线程 - 如果要返回结果,
Runnable不行,得换Callable+Future
真正容易被忽略的是线程上下文:比如 ThreadLocal 变量、事务传播、MDC 日志上下文,在 Runnable 中不会自动继承,需手动传递或使用 InheritableThreadLocal(注意内存泄漏风险)。


















