Runnable是接口,Thread是类;启动线程必须用Thread实例,Runnable仅定义run()任务契约,无start()等线程控制方法,需交由Thread执行。

Runnable 是接口,Thread 是类;想真正启动线程必须用 Thread(或其子类实例),Runnable 本身不能 start。
Runnable 接口只定义了 run() 方法
它纯粹是一个任务契约:告诉 JVM “这个对象里封装了一段要异步执行的代码”。但 Runnable 实例没有线程调度能力,也没有 start()、join()、interrupt() 这些线程控制方法。
常见错误现象:
- 直接调用
myRunnable.run()—— 这只是普通方法调用,仍在当前线程执行,**不会新建线程** - 试图调用
myRunnable.start()—— 编译失败,因为 Runnable 没有该方法
正确做法是把 Runnable 交给 Thread 去执行:
立即学习“Java免费学习笔记(深入)”;
Runnable task = () -> System.out.println("running in thread");
Thread t = new Thread(task);
t.start(); // ✅ 启动新线程,执行 task.run()
Thread 类本身实现了 Runnable 接口
所以 Thread 既是线程载体,又是任务容器。你可以重写它的 run() 方法来定义行为:
Thread t = new Thread() {
public void run() {
System.out.println("anonymous Thread running");
}
};
t.start();
但这种方式耦合度高,且一个 Thread 实例只能启动一次(重复调用 start() 会抛 IllegalThreadStateException)。
关键差异点:
- 继承 Thread 会占用唯一的类继承名额(Java 不支持多继承)
- Runnable 更轻量,适合多个线程共享同一任务逻辑(比如 10 个 Thread 共用同一个
Runnable实例) - 实际开发中几乎总是优先选
Runnable+Thread或线程池,而不是直接继承Thread
线程池只接受 Runnable(或 Callable)
像 ExecutorService.submit()、Executors.newFixedThreadPool(4).execute() 这些 API 的参数类型都是 Runnable 或 Callable,**不接受 Thread 实例**。
原因很直接:线程池管理的是“任务”,不是“线程实体”。它复用内部的 Thread 对象去执行不同 Runnable,如果传入 Thread,就破坏了任务与执行者的解耦。
所以如果你写了个自定义 MyThread extends Thread,想丢进线程池执行——不行,得先提取出它的逻辑,包装成 Runnable。
容易被忽略的生命周期细节
Runnable 实例的状态和生命周期完全由使用者管理;而 Thread 实例自带状态机(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)。调用 t.getState() 查到的是 Thread 的状态,不是 Runnable 的。
另一个坑:匿名内部类或 Lambda 持有外部引用时,可能引发意外的内存泄漏——尤其当 Runnable 被提交到长期存活的线程池,又捕获了 Activity、Context 或大对象时。
别以为用了 Runnable 就安全了;线程模型的复杂性不在接口/类之分,而在共享状态、可见性、中断响应这些地方。










