Java面试必问!run() 和 start() 方法到底有啥区别?

简介: 在多线程编程中,run和 start方法常常让开发者感到困惑。为什么调用 start 才能启动线程,而直接调用 run只是普通方法调用?这篇文章将通过一个简单的例子,详细解析这两者的区别,帮助你在面试中脱颖而出,理解多线程背后的机制和原理。



大家好!我是小米,今天我们来聊一个经常出现在 Java 面试中的经典面试题:线程的 run() 和 start() 有什么区别?为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

这个问题看似简单,其实背后涉及到多线程的运行机制、线程生命周期的管理,甚至是 Java 内部的底层实现。现在,就让我们通过一个故事来一起揭秘这个问题,帮助你在面试中更自信地应对这一难题!

一场关于线程的小故事

假设你正在准备一个大型的在线直播系统,在高并发场景下,你需要确保每个用户的直播流都能够并行处理。为了解决这个问题,你决定在程序中使用 Java 的多线程机制来实现。

你写了一个简单的线程类,代码如下:

这段代码看起来很简单,它实现了 Thread 类并重写了 run() 方法。你可能会想到,调用 run() 方法就可以启动线程并执行直播流处理了。

但是,当你运行下面的代码时,事情变得有点不对劲:

问题来了:你发现程序不会启动新线程,而只是同步执行了 run() 方法里的内容!这究竟是为什么呢?难道 run() 方法有问题吗?我们明明是继承了 Thread 类,重写了 run() 方法,难道这就是启动线程的方式吗?

run() 方法和 start() 方法的区别

在 Java 中,线程的启动不是直接调用 run() 方法就能完成的。实际上,run() 方法是线程的入口点,它包含了线程执行的代码,但它并不会直接启动新的线程。

1、run() 方法

run() 方法只是一个普通的方法,它的作用是描述线程应该执行的任务。如果你直接调用 run() 方法,那么它就像是一个普通的函数调用,在当前线程中执行,而并没有创建新线程。

也就是说,调用 run() 方法时,代码不会在新的线程中执行,而是会在当前线程中顺序执行。所以,在我们上面的代码中,thread.run() 其实就是直接调用了 run() 方法,而没有启动新的线程。

2、start() 方法

而 start() 方法才是启动新线程的正确方式。当我们调用 start() 方法时,它会做两件事:

  • 启动一个新的线程。
  • 在新的线程中调用我们在 run() 方法中写的代码。

总结:start() 方法实际上是一个底层的操作,它会告诉 JVM 创建一个新的线程,并调用 run() 方法。而 run() 方法仅仅是线程执行的入口,它不会自动启动新线程。简单来说,start() 负责启动线程,run() 负责执行线程任务

为什么我们不能直接调用 run() 方法?

那么,问题就来了:既然 run() 方法是线程任务的实现,为什么我们不能直接调用 run() 方法来启动线程呢?这似乎让人摸不着头脑。

原因就在于: 直接调用 run() 方法并不会创建新的线程,它只是将 run() 方法的代码当作普通的方法调用来执行。在这种情况下,线程任务仍然是在当前线程中同步执行的,而不是在新线程中并行执行。

我们回到之前的代码,如果我们像这样调用 run():

这时,run() 方法的代码依然是在主线程中执行的,而不是在新的线程中执行。程序并没有并行地执行线程任务,实际上它只是将线程任务当做普通的任务来执行了。因此,我们看到的输出就是:

它是在主线程中执行的,而不是在新的线程中并行执行的。

start() 方法底层的实现

那我们再来深入探讨一下,start() 方法背后到底做了些什么?如果你深入到 Java 的源代码中,你会发现,start() 方法是调用了 Thread 类中的 start0() 方法,这个方法会在底层启动一个新的操作系统线程,并在新线程中调用 run() 方法。

简单来说:

  • start() 方法:启动一个新的线程,并在该线程中执行 run() 方法。
  • run() 方法:线程的执行内容,是线程开始执行后运行的代码。

线程的生命周期

为了更好地理解 start() 和 run() 的区别,我们还需要了解线程的生命周期。在 Java 中,线程的生命周期一般分为以下几个状态:

  • 新建(New):线程刚被创建,还没有启动。
  • 就绪(Runnable):线程已经启动,但可能还没有开始执行,等待 CPU 分配时间。
  • 运行中(Running):线程获得 CPU 时间片并正在执行任务。
  • 阻塞(Blocked):线程被阻塞,等待某个资源或事件的发生。
  • 终止(Terminated):线程执行完毕,生命周期结束。

start() 方法在底层实际上会让线程进入 就绪 状态,从而使得线程能够进入 运行中 状态并执行 run() 方法。

小结:为什么要使用 start()?

我们回到最初的问题:为什么我们需要使用 start() 方法来启动线程,而不能直接调用 run() 方法?

  • run() 方法是线程的任务代码,它并不会创建新的线程,而是当前线程直接执行。
  • start() 方法负责启动新线程,并且在新线程中调用 run() 方法。

所以,如果我们想要在新的线程中并行执行任务,就必须调用 start() 方法来启动线程,而不能直接调用 run() 方法。直接调用 run() 方法就相当于将线程任务当作普通的函数来执行,根本不会并行运行。

小贴士:多线程编程的常见错误

在实际开发中,我们经常会遇到一些常见的多线程错误,了解并避免这些问题非常重要。

  • 误用 run() 方法:很多初学者误以为调用 run() 方法就能启动线程,其实这只会在当前线程中执行 run() 方法。正确的方式是调用 start()。
  • 线程安全问题:在多线程环境中,多个线程同时操作共享资源时,容易发生线程安全问题。需要使用 synchronized 或者其他线程安全的工具类来避免竞态条件。
  • 忘记线程同步:在一些场景下,多线程共享资源时,如果没有正确同步,可能会导致不可预测的结果。

END

今天我们通过一个简单的故事,深入了解了 Java 中 run() 和 start() 方法的区别,解答了为什么我们调用 start() 方法时会执行 run() 方法,以及为什么不能直接调用 run() 方法来启动线程。掌握这些基本概念,对我们在面试中的表现和日常的多线程开发都大有裨益。

希望这篇文章对你有所帮助,如果你喜欢这样的内容,记得点赞和分享哦!下次我们继续聊更多有趣的 Java 面试题!

公众号对技术型文章的推送机制有所调整,需要大家多多点赞在看转发收藏,才能让更多技术同行们能看到优质的技术分享~

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号软件求生,获取更多技术干货!

相关文章
|
11月前
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
3698 102
|
10月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
10月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
1569 1
|
12月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
513 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
10月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
12月前
|
缓存 Java 关系型数据库
Java 面试经验总结与最新 BAT 面试资料整理含核心考点的 Java 面试经验及最新 BAT 面试资料
本文汇总了Java面试经验与BAT等大厂常见面试考点,涵盖心态准备、简历优化、面试技巧及Java基础、多线程、JVM、数据库、框架等核心技术点,并附实际代码示例,助力高效备战Java面试。
524 0
|
12月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
1577 0
|
Java
JAVA方法的定义
JAVA方法的定义
337 0
|
存储 JSON Java
《从头开始学java,一天一个知识点》之:方法定义与参数传递机制
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问"`a==b`和`equals()`的区别",大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 🚀 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。上篇:《输入与输出:Scanner与System类》 | 下篇剧透:《方法重载与可变参数》。
408 25