继承Thread最直接但不灵活,因Java单继承限制且无法复用线程;实现Runnable是主流推荐方式,解耦任务与线程管理;Callable+Future适用于需返回值或异常处理的场景;生产环境必须使用线程池(ExecutorService)统一管理生命周期。

继承 Thread 类是最直接但最不灵活的方式
直接让自定义类 extends Thread,重写 run() 方法,然后调用 start() 启动线程。这种方式简单直观,但 Java 不支持多重继承,一旦类已继承其他父类,就无法再继承 Thread。
常见错误是误调用 run() 而非 start() —— 这会导致方法在当前线程同步执行,根本没产生新线程。
-
start()触发 JVM 创建并调度新线程,之后自动调用run() - 子类中若需访问线程名、优先级等,可直接用
this.getName()、this.setPriority()等 - 无法复用已有线程对象:每个
Thread实例只能start()一次,重复调用抛IllegalThreadStateException
实现 Runnable 接口是推荐的主流做法
Runnable 是函数式接口,只需实现 run() 方法,再将其传给 Thread 构造器或线程池。它解耦了任务逻辑与线程生命周期管理,支持多继承(类可同时 implements Runnable 并继承其他类)。
注意:Runnable 没有返回值,也不能抛受检异常(Exception),这是它和 Callable 的关键区别。
立即学习“Java免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 适合「只执行、不关心结果」的场景,比如日志刷盘、心跳上报
- 可被多个
Thread实例共享,天然支持任务复用(但要注意共享状态的线程安全) - 传入线程池时,实际执行的是
ThreadPoolExecutor.execute(Runnable),不是submit()
使用 Callable + Future 获取带返回值的异步结果
当需要线程执行后返回结果或捕获异常时,必须用 Callable 替代 Runnable。它定义了 call() 方法,允许返回泛型值、抛出任意异常。
但 Callable 不能直接交给 Thread,必须配合 FutureTask(包装成 Runnable)或更常用的是线程池的 submit() 方法。
-
Future.get()是阻塞调用,超时未完成会抛TimeoutException,务必处理中断(InterruptedException) - 线程池
submit(Callable)返回Future,底层实际创建的是FutureTask对象 - 如果任务本身耗时长且无需结果,强行用
Callable反而增加封装开销,不如用Runnable
用线程池(ExecutorService)管理线程生命周期才是生产环境标配
手动 new Thread().start() 在高并发下极易导致 OOM(线程数爆炸)或上下文切换开销过大。所有真实项目都应使用 ExecutorService,例如 Executors.newFixedThreadPool(4) 或更稳妥的 ThreadPoolExecutor 自定义构造。
四种“实现方式”里,只有线程池是真正面向工程落地的方案;其余三种本质都是定义任务(Runnable/Callable),而线程池负责执行、复用、拒绝、监控。
-
execute(Runnable)用于无返回值任务;submit()支持Runnable和Callable,返回Future - 别用
Executors工厂方法创建无界队列的线程池(如newCachedThreadPool),容易堆内存溢出 - 关闭线程池必须调用
shutdown()+awaitTermination(),否则 JVM 可能不退出
真正复杂的点不在“怎么写个线程”,而在于任务划分粒度、共享状态的可见性与原子性、线程池参数与业务吞吐的匹配——这些不会因为选了 Runnable 还是 Callable 就自动解决。

















