当前位置: 首页 > news >正文

【Java 课程作业】继承 Thread 类与实现 Runnable 接口创建线程的区别详解

本文是 Java 面向对象程序设计课程的课后作业,详细讲解 Java 中两种最基础的线程创建方式:继承 Thread 类实现 Runnable 接口。通过代码示例、经典售票案例和多维度对比,深入分析两者的本质区别、各自的优缺点以及实际开发中的选择原则。

文章目录

    • 一、Java 创建线程的两种基础方式
    • 二、两种方式的代码实现
      • 2.1 方式一:继承 Thread 类
      • 2.2 方式二:实现 Runnable 接口
    • 三、核心区别多维度对比
      • 3.1 对比总表
      • 3.2 详细解读
        • 1. 单继承限制(最本质区别)
        • 2. 资源共享能力
          • ❌ 继承 Thread 实现(需要用静态变量共享)
          • ✅ 实现 Runnable(天然共享)
        • 3. 代码解耦与复用
        • 4. 与线程池的兼容性
    • 四、开发中如何选择?
      • 推荐原则
      • 行业共识
    • 五、学习总结

一、Java 创建线程的两种基础方式

在 Java 中,创建线程最常用的有两种方式:

  1. 继承java.lang.Thread,重写run()方法
  2. 实现java.lang.Runnable接口,实现run()方法,再将其作为参数传给 Thread 对象

很多初学者会疑惑:两种方式都能创建线程,它们到底有什么区别?为什么老师总说推荐使用Runnable接口?下面我们通过代码和案例逐一分析。


二、两种方式的代码实现

2.1 方式一:继承 Thread 类

实现步骤

  1. 定义一个类继承Thread
  2. 重写run()方法,编写线程执行的任务逻辑
  3. 创建该子类的实例对象
  4. 调用start()方法启动线程
/** * 方式一:继承Thread类创建线程 */publicclassMyThreadextendsThread{// 构造方法:设置线程名publicMyThread(Stringname){super(name);}// 重写run方法,编写线程任务@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+" 执行:"+i);try{// 模拟线程执行耗时Thread.sleep(200);}catch(InterruptedExceptione){e.printStackTrace();}}}// 测试publicstaticvoidmain(String[]args){// 创建两个线程对象MyThreadt1=newMyThread("线程A");MyThreadt2=newMyThread("线程B");// 启动线程(注意:调用start()才会启动新线程,调用run()只是普通方法调用)t1.start();t2.start();}}

2.2 方式二:实现 Runnable 接口

实现步骤

  1. 定义一个类实现Runnable接口
  2. 实现run()方法,编写线程执行的任务逻辑
  3. 创建该实现类的实例
  4. 将实例作为参数传入Thread的构造方法,创建 Thread 对象
  5. 调用 Thread 对象的start()方法启动线程
/** * 方式二:实现 Runnable 接口创建线程 */publicclassMyRunnableimplementsRunnable{// 实现run方法,编写线程任务@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+" 执行:"+i);try{Thread.sleep(200);}catch(InterruptedExceptione){e.printStackTrace();}}}// 测试publicstaticvoidmain(String[]args){// 创建任务对象MyRunnabletask=newMyRunnable();// 将任务对象传给Thread,创建多个线程执行同一个任务Threadt1=newThread(task,"线程A");Threadt2=newThread(task,"线程B");t1.start();t2.start();}}

注意:Runnable接口只有一个run()方法,它本身不具备启动线程的能力,最终还是要靠Thread类的start()方法来启动线程。Runnable的作用是封装线程要执行的任务


三、核心区别多维度对比

3.1 对比总表

对比维度继承 Thread 类实现 Runnable 接口
本质继承一个线程类实现一个任务接口
单继承限制受 Java 单继承限制,子类不能再继承其他类不受单继承限制,还可以同时继承其他类、实现其他接口
资源共享多个线程间难以共享资源,需借助静态变量天然支持多个线程共享同一个任务对象中的资源
代码耦合度线程对象和任务逻辑绑定在一起,耦合度高任务逻辑与线程对象分离,解耦性好
代码复用性任务只能被该线程类使用,复用性差任务可以被多种线程、线程池复用,复用性强
线程池支持不支持直接放入线程池执行完美兼容线程池,是实际开发的标准用法
适用场景简单的单线程场景、无需共享资源的场景多线程共享资源、复杂业务、实际项目开发

3.2 详细解读

1. 单继承限制(最本质区别)

Java 是单继承语言,一个类只能有一个直接父类。如果选择继承Thread类,那么这个类就不能再继承其他任何类了,这会大大限制类的扩展性。

而实现Runnable接口完全没有这个问题,一个类可以同时实现多个接口,还可以继承其他父类,灵活性更高。

2. 资源共享能力

这是两者最直观的区别。实现 Runnable 接口可以很方便地让多个线程共享同一份资源,而继承 Thread 类做不到这一点。

我们用经典的“多窗口售票”案例来演示:

案例需求:3 个窗口同时卖 100 张票,总票数是共享的。

❌ 继承 Thread 实现(需要用静态变量共享)
publicclassTicketThreadextendsThread{// 必须用 static 静态变量,才能让多个线程对象共享privatestaticinttickets=100;publicTicketThread(Stringname){super(name);}@Overridepublicvoidrun(){while(tickets>0){System.out.println(getName()+" 卖出第 "+tickets--+" 张票");try{Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}}}publicstaticvoidmain(String[]args){// 每个线程对象都有自己的成员变量,必须靠 static 共享newTicketThread("窗口1").start();newTicketThread("窗口2").start();newTicketThread("窗口3").start();}}
✅ 实现 Runnable(天然共享)
publicclassTicketRunnableimplementsRunnable{// 普通成员变量即可,多个线程共享同一个对象privateinttickets=100;@Overridepublicvoidrun(){while(tickets>0){System.out.println(Thread.currentThread().getName()+" 卖出第 "+tickets--+" 张票");try{Thread.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}}}publicstaticvoidmain(String[]args){// 只创建一个任务对象,交给3个线程执行TicketRunnabletask=newTicketRunnable();newThread(task,"窗口1").start();newThread(task,"窗口2").start();newThread(task,"窗口3").start();}}

对比结论

  • 继承 Thread 的方式必须借助static静态变量才能共享资源,而静态变量生命周期长,容易带来其他问题
  • 实现 Runnable 的方式只需要创建一个任务对象,多个线程共享该对象的成员变量,更加自然合理
3. 代码解耦与复用
  • 继承Thread:线程对象和任务逻辑绑定死了,一个线程类只能做一件事,复用性差
  • 实现Runnable:把「线程要执行的任务」单独封装成一个对象,和线程本身解耦。同一个任务可以交给不同的线程执行,也可以交给线程池执行,复用性极强
4. 与线程池的兼容性

在实际企业开发中,我们几乎不会手动 new Thread,而是使用线程池来管理线程。而线程池只能接收RunnableCallable类型的任务,不能直接接收 Thread 子类。

从这一点来说,实现 Runnable 接口是工业界的标准做法,而继承 Thread 更多只出现在入门教学中。


四、开发中如何选择?

推荐原则

  1. 优先选择实现 Runnable 接口

    这是绝大多数场景下的最佳选择,尤其是在需要多线程共享资源、需要良好扩展性、或者配合线程池使用的场景。

  2. 什么时候可以用继承 Thread?

    • 非常简单的单线程场景,只是为了快速测试
    • 该类本身就是一个线程实体,不需要再继承其他类
    • 教学演示、入门练习

行业共识

在真实的 Java 后端开发中,99% 的场景都会使用 Runnable(或 Callable)来定义线程任务,配合线程池来执行。直接继承 Thread 类创建线程的写法几乎不会出现在生产代码中,因为它的扩展性差、资源浪费严重,也不利于线程管理。


五、学习总结

通过这次作业,我搞懂了继承Thread和实现Runnable这两种创建线程方式的区别。以前只知道两种写法都能运行线程,不知道背后的设计思想和适用场景差异这么大。

我最大的收获有两点:

  1. 面向接口编程的优势Runnable接口完美体现了面向接口编程的思想,它把「线程执行的任务」和「线程本身」分离开,带来了更好的扩展性、复用性和解耦性。
  2. 资源共享的设计差异:通过售票案例的对比,我直观地感受到了两种方式在资源共享上的区别,也理解了为什么实际开发都推荐用Runnable

如果觉得这篇文章对你有帮助,欢迎点赞、收藏、关注!有任何问题可以在评论区留言交流~

http://www.jsqmd.com/news/1091110/

相关文章:

  • Python开发实习生指南:简历投递、实习内容与个人项目的本质区别
  • 终极Dify工作流宝库:让AI应用开发像搭积木一样简单
  • 深度学习周报(6.22~6.28)
  • 性价比高的捆扎绳服务周到的公司
  • JavaEE安全纵深防御:JNDI注入攻防演进与高版本JDK绕过实战
  • AI Agent:从RAG到多智能体
  • Gliding Horse 工具结果压缩体系:如何用“指针”驯服上下文膨胀
  • Win11Debloat终极指南:3步快速清理Windows系统,提升70%性能!
  • Agent后端-记忆RAG和上下文管理怎么做才像样
  • 导师严选!2026年性价比拉满的专业降AIGC软件
  • 实战深度解析:Unitree RL GYM如何实现机器人策略的多仿真环境无缝迁移
  • ada_demo_gprbuild demo.gpr操作流程
  • MiniMax M2.7自我进化机制解析:运行时反馈、研究代理与权重更新三闭环
  • 自进化的 Agent 记忆层 —— PowerMem 简易操作手册
  • I2C目标模式陈旧数据刷新与FIFO中断DMA协同实战
  • iot访问控制:模型公式、风险分析与工程落地
  • 如何高效解决macOS安全限制:SMAPI模组加载器完整配置方案
  • 7种客户知识分享策略,Baklib同源多站发布让效率翻倍
  • 图片太大怎么缩小
  • 第14讲|需求不清晰时,如何让 AI 反向追问
  • 看书---------
  • HTML 的 <button> 元素
  • 2026汤阴中古风家具厂家推荐,挑对源头少花冤枉钱
  • 易语言大漠插件模块实战:精准定位窗口句柄的FindWindow系列命令封装
  • OneDrive完全卸载工具:彻底清理Windows系统性能拖累
  • Qualcomm AiHub
  • 如何用5个步骤彻底优化你的Windows系统:Winhance中文版完全指南
  • Win11Debloat:3分钟让你的Windows 11告别卡顿与隐私困扰
  • 第七周学习笔记
  • 包装印刷交货准时性分析:从沟通效率到色彩还原的技术评估