推荐实现 runnable 接口而非继承 thread 类,因后者不支持多重继承、扩展性差且易混淆 start() 与 run();runnable 解耦执行逻辑与环境,支持 lambda、复用及线程池,而需返回值或异常处理时应选 callable。

继承 Thread 类:能用,但不推荐
直接继承 Thread 类写法简单,但 Java 不支持多重继承,一旦你这个类还需要继承其他业务父类(比如 UserService、BaseActivity),就彻底卡死。而且 Thread 本身是个重量级类,包含大量线程控制逻辑,你只是想跑个任务,却被迫“扛起整个线程的躯干”。
常见错误现象:new MyThread().start() 没问题,但 new MyThread().run() 只是普通方法调用,不会开启新线程——这点新手常混淆。
- 只在极简 demo 或教学场景中临时用,别进生产代码
- 子类无法再继承其他类,扩展性归零
-
Thread的start()和run()行为差异必须记牢:前者触发 JVM 线程调度,后者只是同步执行
实现 Runnable 接口:标准解法,99% 场景该选它
Runnable 是函数式接口(Java 8+),只定义一个 run() 方法,纯粹表达“我要执行什么”,和“谁来执行”完全解耦。你可以把它丢给 Thread、ExecutorService、甚至 ForkJoinPool,自由度高得多。
使用场景:定时任务、异步日志、UI 耗时操作、任何需要把逻辑和执行环境分离的地方。
立即学习“Java免费学习笔记(深入)”;
- 支持 lambda 表达式:
new Thread(() -> System.out.println("ok")).start(); - 可被多次提交:同一个
Runnable实例能反复交给不同线程池执行;而Thread实例只能start()一次,重复调用抛IllegalThreadStateException - 天然符合“单一职责”:你的业务类只需关注“做什么”,不用管“怎么调度”
Runnable vs Callable:要不要返回值和异常处理
如果任务需要返回结果或可能抛受检异常,Runnable 就不够用了——它的 run() 方法既不能返回值,也不能声明抛出异常。
这时该换 Callable:call() 方法返回泛型值,且允许抛任意异常。但它不能直接传给 Thread 构造器,必须配合 FutureTask 或 ExecutorService.submit() 使用。
-
Runnable→ 适合“发个通知”“刷个缓存”这类无果、无异常风险的操作 -
Callable→ 适合“查数据库”“调远程接口”这类要结果、要异常反馈的任务 - 性能影响几乎可忽略,但
Future.get()是阻塞的,别在线程池核心线程里傻等
别踩这些坑:线程创建不是终点,资源和生命周期才是
很多人写了 Runnable、起了线程,就以为完事了。其实真正出问题的,往往在之后:共享变量没加锁、线程池没 shutdown、异常吞掉没日志、或者 ThreadLocal 泄漏。
- 永远优先用
ExecutorService,而不是new Thread():避免无限创建线程打爆内存 - 实现
Runnable时,若访问外部对象(比如list、map),确认是否线程安全;别默认“我只读”——JVM 指令重排可能让你读到半初始化状态 -
Thread子类里覆写run()却忘了调用super.run()?没用,Thread.run()默认空实现,不影响,但容易误导自己
最易被忽略的一点:线程名。不设名的线程在堆栈、监控、日志里全显示为 Thread-1、Thread-2,排查时根本分不清是谁干的。起线程前务必 thread.setName("order-timeout-checker")。










