在 Java 多线程入门学习中,继承 Thread 子类实现 Runnable 接口配合 Thread是最基础的两种线程创建方案,很多初学者刚接触时容易混淆两者使用场景与底层差异。本文从实现原理、代码示例、优缺点、适用场景四个维度拆解区别,帮你彻底理清选型逻辑。

一、两种方式代码实现演示

1. 方式一:继承 Thread 类创建线程

原理:自定义类继承java.lang.Thread,重写run()方法(线程任务体),实例化子类对象后调用start()启动线程。

java

// 自定义线程类,继承Thread
class MyThread extends Thread {
    // 重写run:线程执行的业务逻辑
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        // 创建线程实例
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        // 启动线程,自动执行run()
        t1.start();
        t2.start();
    }
}

关键:start()会向 JVM 申请开启操作系统线程,由 JVM 自动回调run();直接调用run()只是普通方法调用,不会新建线程。

2. 方式二:实现 Runnable 接口创建线程

原理:自定义类实现Runnable接口、重写run()存放任务,将 Runnable 实现类作为参数传入 Thread 构造器,借助 Thread 对象start()启动线程。

// 任务类,实现Runnable接口,只负责封装任务
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        // 同一个任务实例
        MyRunnable task = new MyRunnable();
        // 多个线程共用同一个任务对象
        Thread t1 = new Thread(task, "线程A");
        Thread t2 = new Thread(task, "线程B");
        t1.start();
        t2.start();
    }
}

二、核心五大区别详解

1. 继承限制不同(最关键区别)

  • 继承 Thread:Java 是单继承,一旦类继承Thread,就无法再继承其他父类,继承链被锁死,扩展性差。
  • 实现 Runnable:接口是多实现,实现Runnable后,类还能正常继承别的父类、实现其他接口,规避单继承局限性,符合面向接口编程思想。

2. 资源共享能力不同

  • 继承 Thread:每次创建new MyThread()都是全新对象,每个线程独占成员变量,很难实现多线程共享同一份资源;如需共享只能用静态变量,容易引发线程安全隐患。
  • 实现 Runnable:多个Thread对象可以传入同一个 Runnable 任务实例,天然共享任务类中的成员变量,资源共享简单便捷,适合售票、抢券等多线程操作同一资源场景。

示例:多线程卖票(Runnable 优势)

class TicketTask implements Runnable {
    private int ticket = 10; // 共享票数
    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println(Thread.currentThread().getName()+"卖第"+ticket--+"张票");
        }
    }
}
// 三个线程共用10张票资源
Thread t1=new Thread(new TicketTask(),"窗口1");
Thread t2=new Thread(new TicketTask(),"窗口2");
Thread t3=new Thread(new TicketTask(),"窗口3");

3. 职责分层不同

  • Thread 继承线程对象 + 业务任务耦合在同一个类中,线程创建和任务逻辑绑定,不符合单一职责原则,代码臃肿。
  • Runnable 实现任务与线程分离:Runnable 只封装业务任务,Thread 只负责线程调度与资源创建,解耦设计,方便任务复用。

4. 代码复用与灵活性

  • Thread 子类:任务绑定线程本身,任务无法脱离线程单独复用;
  • Runnable:任务是独立对象,既能交给 Thread 运行,也能配合线程池ExecutorService使用,是线程池、Lambda 表达式底层默认规范。

5. Lambda 适配(JDK8+)

Runnable 是函数式接口(只有一个抽象方法 run),JDK8 后可直接用 Lambda 简写代码,无需单独创建实现类:

java

运行

// 一行代码创建线程
new Thread(()-> System.out.println("Lambda创建Runnable线程")).start();

而继承 Thread 无法使用 Lambda 简化。

三、优缺点总结

表格

对比项 继承 Thread 类 实现 Runnable 接口
继承约束 受 Java 单继承限制,不能再继承其他类 无继承限制,可多实现、多继承
资源共享 多线程资源共享麻烦(依赖 static) 天然支持多线程共享同一个任务资源
代码耦合 任务和线程对象耦合,单一职责差 任务、线程解耦,扩展性强
适用场景 简单独立任务、不需要资源共享的小程序 绝大多数生产环境、资源共享、线程池开发
Lambda 支持 不支持 支持 Lambda 简写

1. Thread 继承优缺点

✅ 优点:代码写法简单,直接 new 子类调用 start; ❌ 缺点:单继承受限、资源共享困难、耦合度高,项目开发极少使用

2. Runnable 实现优缺点

✅ 优点:解耦、无继承限制、资源共享方便、适配线程池与 Lambda,企业开发首选; ❌ 缺点:代码相比继承多一层封装,新手初期理解略繁琐。

四、生产环境选型建议

  1. 优先使用实现 Runnable 方式:项目开发 95% 场景选用,尤其是多线程处理同一资源、配合线程池开发时;
  2. 仅简单测试小 Demo 用 Thread 继承:日常练习、一次性简单任务,追求代码极简时临时使用;

补充:除了两种基础方式,Java 还有Callable+FutureTask创建带返回值线程,本质底层依然依托 Runnable 思想。

五、拓展延伸:源码底层逻辑

Thread 类源码中存在private Runnable target;成员变量:

  • 当传入 Runnable 构造时,target 赋值为传入任务,start()启动后 JVM 回调run()会执行target.run()
  • 继承 Thread 重写 run 时,直接覆盖父类 run 方法,忽略 target,所以两种方式最终都是执行 run 方法,只是任务来源不同。

结语

多线程开发中优先 Runnable是行业共识,本质是面向接口、解耦编程思想的体现。掌握两者区别不仅应付面试高频考题,更是后续学习线程池、并发工具的基础。

更多推荐