Java 创建线程两种方式:继承 Thread 类与实现 Runnable 接口的区别详解
一、两种创建方式的基础设计思路
继承 Thread 子类创建线程
这种方式的核心逻辑是把线程本身和要执行的业务任务整合在同一个类里。我们自定义一个类去继承 Java 自带的 Thread 父类,再重写父类里的 run 方法,把需要线程运行的业务逻辑写进这个方法中。使用时直接实例化自定义子类对象,调用 start 方法就能启动线程,相当于一个类既充当了线程载体,又承载了业务代码。
实现 Runnable 接口创建线程
该方式采用职责拆分的设计思想,将业务任务和线程运行载体拆分开。首先编写一个类实现 Runnable 接口,在实现的 run 方法中编写纯业务逻辑,这个类只负责定义要完成的任务,不具备线程调度能力。之后把这个任务类的实例传入 Thread 类的构造方法,借助 Thread 类来创建、启动线程,由 Thread 负责底层的线程调度,Runnable 只专注业务实现。
二、核心区别详细解读
- 继承层面的限制不同
Java 语言采用单继承机制,一个类只能有一个直接父类。如果选择继承 Thread 类创建线程,那么这个自定义类后续无法再继承其他业务父类,会极大限制类的拓展性,尤其在分层开发、框架整合的项目中,很容易出现继承冲突的问题。
而实现 Runnable 接口没有这类限制,Java 支持一个类同时实现多个接口,即便已经实现了 Runnable,依旧可以继承其他父类,或是实现其余业务接口,完全不会占用继承名额,架构设计上更加灵活。 - 多线程资源共享能力不同
采用继承 Thread 子类的写法时,每创建一个线程都需要 new 一个全新的自定义子类对象,每个对象都会独立拥有自身的成员变量,不同线程之间无法共用同一份数据资源。比如模拟多窗口卖票场景,用这种方式会让每个售票窗口都拥有完整的票数数据,出现总票数重复售卖的错误,不符合真实业务需求。
依托 Runnable 接口实现的方式则天然支持资源共享,我们只需要创建一个 Runnable 任务实例,再把这同一个实例传入多个 Thread 对象中,所有线程都会共用任务实例里的成员变量,能轻松实现多线程操作同一份共享数据,是并发资源操作场景的首选。 - 代码耦合与复用性不同
继承 Thread 的写法将线程运行能力和业务逻辑高度耦合,业务代码被牢牢绑定在 Thread 子类中,这个类只能作为线程来使用,无法脱离线程场景被其他模块调用,代码复用范围极窄。
实现 Runnable 接口实现了解耦设计,业务任务被封装在 Runnable 实现类中,和 Thread 线程载体完全分离。编写好的任务可以交给普通 Thread 运行,也能直接放入线程池、定时任务组件等各类并发工具中执行,脱离线程场景也能复用业务逻辑,代码通用性更强,也符合面向接口编程的开发思想。 - 底层本质与官方推荐程度不同
从源码底层来看,Thread 类本身就实现了 Runnable 接口,继承 Thread 子类本质上是对 Runnable 实现方式的一层封装,只是简化了初学者的编写步骤,并没有改变底层运行逻辑。
在实际企业开发中,官方一直推荐使用实现 Runnable 接口的方式创建线程。一方面它规避了单继承的短板,另一方面贴合软件设计的开闭原则,后续修改业务逻辑仅需调整 Runnable 实现类,无需改动线程创建的整体架构,后期维护和迭代成本更低。
三、开发场景选型总结
绝大多数生产环境下,都要优先选择实现 Runnable 接口的方式创建线程,不管是复杂业务项目、需要资源共享的并发场景,还是后期要接入线程池等高级并发工具,该方式都能完美适配。
只有在极其简单的一次性测试场景、没有拓展需求、也不需要共享资源的极简代码中,才可以临时使用继承 Thread 子类的写法,正式项目里基本不会采用这种方案。
浙公网安备 33010602011771号