Java 线程创建:继承Thread子类 vs 直接new Thread,到底有什么区别?
Java 线程创建:继承Thread子类 vs 直接new Thread,到底有什么区别?
在 Java 多线程基础学习和面试中,最经典的问题之一:
继承 Thread 类创建线程 和 直接 new Thread() 传入 Runnable 创建线程,有什么本质区别?
很多同学只会写代码,但说不清底层差异、设计缺陷、适用场景,面试极易扣分。
今天我用一篇博客,彻底讲透两种写法的语法、原理、优缺点、资源共享、设计思想、企业最佳实践。
一、两种线程创建方式代码还原
1. 方式一:自定义类继承 Thread 子类
核心逻辑:继承 Thread 类,重写 run() 方法,线程对象本身就是任务。
// 自定义线程类,继承Thread
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行的任务逻辑
for (int i = 0; i < 5; i++) {
System.out.println("线程执行:" + Thread.currentThread().getName() + " i=" + i);
}
}
}
// 测试
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
特点:线程对象和任务耦合在一起。
2. 方式二:直接 new Thread + Runnable
核心逻辑:Thread 只负责线程调度,Runnable 专门负责任务逻辑,线程和任务解耦。
public class RunnableTest {
public static void main(String[] args) {
// 定义独立任务
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程执行:" + Thread.currentThread().getName() + " i=" + i);
}
};
// 多个线程共用同一个任务
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
二、核心区别对照表(面试必背)
|
对比维度 |
继承 Thread 子类 |
new Thread + Runnable |
|---|---|---|
|
设计思想 |
继承(is-a) |
组合(has-a) |
|
耦合度 |
高(线程=任务) |
低(线程、任务分离) |
|
Java单继承限制 |
有,无法再继承其他类 |
无,可正常继承、多实现接口 |
|
任务共享 |
困难,每个线程独立任务 |
轻松,多线程共享同一个Runnable |
|
代码复用性 |
差 |
高,任务可复用、可交给线程池 |
|
扩展性 |
差 |
强,适配线程池、Callable |
|
开发推荐 |
不推荐 |
✅ 官方推荐 |
三、深度原理讲解
1. 继承 Thread 的最大缺陷:单继承限制
Java 是单继承机制:
如果你的类 extends Thread,就不能再继承其他业务父类。
在业务开发中,实体类、工具类、业务父类非常普遍,一旦继承 Thread,直接废掉类的扩展性。
而 Runnable 是接口,一个类可以:
-
继承一个父类
-
同时实现多个接口
完全不影响业务结构。
2. 任务与线程是否解耦,是核心差距
继承 Thread 方式:
任务逻辑写在 Thread 子类内部,任务属于线程,任务不能单独剥离复用。
Runnable 方式:
任务是独立的 Runnable 对象,线程只是一个“执行者”。
同一个任务可以:
-
被多个线程并行执行
-
交给线程池执行
-
随时替换、修改
3. 资源共享场景(面试高频)
需求:三个线程同时卖 100 张票(共享资源)。
✅ Runnable 轻松实现共享
class TicketTask implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 卖票:" + ticket--);
}
}
}
// 三个线程共享同一个任务对象
public class Test {
public static void main(String[] args) {
TicketTask task = new TicketTask();
new Thread(task,"窗口1").start();
new Thread(task,"窗口2").start();
new Thread(task,"窗口3").start();
}
}
❌ 继承 Thread 无法直接共享
每 new 一次子类,就是一个独立线程对象,变量不共享,必须加 static 静态变量强行共享,代码丑陋且线程不安全。
四、常见误区:run() 和 start() 千万别混用
两种创建方式通用规则:
-
start():调用 native 方法,向操作系统申请开启新线程,最终执行 run(),真正多线程
-
run():只是普通成员方法调用,不开启新线程,串行执行
面试高频坑:直接调用 run() 不会创建新线程!
五、企业开发最终结论
✅ 永远推荐:Thread + Runnable(Lambda)
理由:
-
符合 组合优于继承 的设计思想
-
规避单继承限制,类结构更优雅
-
任务与线程解耦,复用性极强
-
天然支持多线程资源共享
-
无缝对接线程池、Future、异步任务
❌ 禁止使用:继承 Thread 子类
除了教学演示,实际项目中几乎没有使用场景。
六、全文总结
1. 继承 Thread 是继承耦合,受单继承限制,无法共享任务,复用性差,不推荐;
2. 直接 new Thread + Runnable 是组合解耦,线程和任务分离,支持资源共享、高复用、易扩展;
3. 开发统一标准:一律使用 Runnable / 线程池,禁止继承 Thread;
4. 开启线程必须调用 start(),不能直接调用 run()。
看似简单的线程创建方式,背后是 Java 优秀的设计思想:组合优于继承、职责单一、解耦复用。掌握这两种方式的区别,是学好多线程、搞定面试、写出高质量并发代码的基础。
更多推荐
所有评论(0)