在 Java 多线程编程中,创建线程最基础、最常用的两种方式就是:继承 Thread 类 和 实现 Runnable 接口。虽然它们最终都能创建出并发执行的任务,但在实际开发中,它们的底层逻辑、灵活性以及内存共享机制有着很大的区别。本文将带你深度对比这两种方式,帮你彻底搞懂它们的区别与选型。一、 两种创建方式的代码实现
**1. 继承 Thread 类通过继承 Thread 类并重写 run() 方法:
**// 1. 定义一个继承自 Thread 的子类
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在运行…");
}
}

public class Main {
public static void main(String[] args) {
// 2. 创建子类实例并启动
MyThread t1 = new MyThread();
t1.start();
}
}
2. 实现 Runnable 接口(直接创建 Thread)通过实现 Runnable 接口,然后将其实例作为参数传递给 Thread 构造函数:
// 1. 定义一个实现 Runnable 接口的类
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在运行…");
}
}

public class Main {
public static void main(String[] args) {
// 2. 创建实现类实例
MyRunnable runnable = new MyRunnable();
// 3. 将其作为参数传给 Thread 构造并启动
Thread t1 = new Thread(runnable);
t1.start();

    // 现代玩法:也可以直接使用 Lambda 表达式
    Thread t2 = new Thread(() -> System.out.println("Lambda 线程运行中..."));
    t2.start();
}

}
二、 核心区别对比为了让你一目了然,我们从四个维度来进行对比:对比维度继承 Thread 类实现 Runnable 接口单继承限制受限。Java 仅支持单继承,继承了 Thread 就无法再继承其他业务父类。灵活。实现接口的同时,依然可以继承其他父类,扩展性更好。数据与资源共享较难共享。每个线程对象都是独立的实例,其内部成员变量默认不共享(除非用 static)。天然共享。多个 Thread 对象可以传入同一个 Runnable 实例,从而轻松共享该实例的成员变量。设计模型 (解耦)任务代码与线程控制耦合在一起。实现了任务(Runnable)与线程驱动(Thread)的分离,符合面向对象的设计原则。与线程池的结合线程池无法直接接收 Thread 子类。完美契合。现成的线程池(如 ExecutorService)主要接收 Runnable/Callable 任务。三、 深入:资源共享差异演示(以卖票为例)这是理解两者区别最经典的案例。假设我们要写一个“3个窗口共同卖 5 张票”的程序:❌ 继承 Thread 类的局限如果用 Thread 子类,每个线程都会拥有自己独立的 5 张票:Javaclass TicketThread extends Thread {
private int tickets = 5; // 每个实例都有独立的5张票
@Override
public void run() {
while (tickets > 0) {
System.out.println(getName() + " 卖出一张票,剩余: " + (–tickets));
}
}
}
// 测试:总共会卖出 15 张票,没有达到“共同卖5张票”的目的
实现 Runnable 接口的优势多个线程可以共享同一个 Runnable 实例,从而共享同一份票源:Javaclass TicketRunnable implements Runnable {
private int tickets = 5; // 共享的票源
@Override
public void run() {
while (tickets > 0) {
// 注:此处实际开发需加锁,这里仅演示资源共享概念
System.out.println(Thread.currentThread().getName() + " 卖出一张票,剩余: " + (–tickets));
}
}
}
// 测试:三个线程传入同一个 TicketRunnable 实例,总共只卖出 5 张票。
四、 总结与选型建议在实际的 Java 开发中,强烈推荐优先使用实现 Runnable 接口(或 Callable 接口)的方式。为什么?避开单继承坑:保证了类的扩展性。实现解耦:把“要做什么任务(Runnable)”和“怎么去执行(Thread)”分离开来。拥抱高并发:在实际开发中,我们几乎不会手动 new Thread(),而是使用线程池。线程池的核心就是执行 Runnable 任务。

更多推荐