[面试] Thread 中 start() 和 run() 的区别都不知道,还怎么混?

作者分享面试Java工程师的心得,以面试中询问Thread类start()和run()方法区别为例,分析求职者答不上问题的影响。接着介绍线程状态、启动线程方式,通过例子和源码阐释两方法区别,最后总结start()能启动新线程、可实现多线程,run()则不能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引子

最近面试了不少 Java 工程师,有一些心得体会想给大家分享,比如,上次就有一个小哥被我 “送走” 了,我尽量复原一下当时的面试情景,对话大致如下:

我:我们知道,JDK 中的线程 Thread 类有两个方法,一个叫 start(),一个叫 run(),那你知道两者有什么区别吗?

小哥:额......好像都是运行...运行一个线程吧......

我:那都是运行一个线程的话,设计成一个方法就好了,没必要两个方法吧?

小哥:这......不太清楚......

我:好,今天就这样吧...(起身)

【PS:当然,最后这个起身动作是前面已经问了好几个问题之后产生的。】

分析

众所周知,Java 面试一般都逃不开多线程的题目。我一般会从简单的开始问,如果答得上来就继续追问,几番深入浅出之后对求职者的掌握程度就有一个基本的判定了。

如果求职者连最简单的问题都答不上来,那我就会觉得要么基础太差,要么对此次面试没有给予足够的重视去作准备。不管是何种原因,在面试官心中都是掉分的,结果也可想而知。

原理

好,回过神来,再敲一下黑板:Thread 类中的 start() 和 run() 到底有什么不一样?

在正式学习 Thread 类中的这两方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对 Thread 类中的方法的理解。

  • 创建(new)状态: 准备好了一个多线程的对象
  • 就绪(runnable)状态: 调用了 start() 方法, 等待 cpu 进行调度
  • 运行(running)状态: 执行 run() 方法
  • 阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
  • 终止(dead)状态: 线程销毁

实现并启动线程的两种方式,这可是我们背得滚瓜烂熟的知识点:

  • 编写一个类继承自 Thread 类,重写 run() 方法。用 start() 方法启动线程
  • 编写一个类实现 Runnable 接口,实现 run() 方法。用 new Thread(Runnable target).start() 方法来启动线程

为了分清 start() 方法调用和 run() 方法调用的区别,请看下面一个例子:

package cn.java.basic;

/**
 * @author Jessehuang
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("主线程ID:" + Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}

class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:" + name + " 子线程ID:" + Thread.currentThread().getId());
    }
}

运行结果:

主线程ID:1
name:thread2 子线程ID:1
name:thread1 子线程ID:10

从输出结果可以得出以下结论:

1)thread1 和 thread2 的线程 ID 不同,thread2 和主线程 ID 相同,说明通过 run() 方法调用并不会创建新的线程,而是在主线程中直接运行 run() 方法,跟普通的方法调用没有任何区别。

2)虽然 thread1 的 start 方法调用在 thread2 的 run() 方法前面调用,但是先输出的是 thread2 的 run() 方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

对于 Thread 类中 start() 和 run() 的源码,这里作一个粗浅的说明:start() 方法调用了本地 native 的 start0(),而 run() 就是直接用重写了 Thread 实现的 Runnable 的 run()。比较简单,就不贴了,可自行在 JDK 查阅。

OK,我们稍作整理成较为正式(但略显枯燥的)文字,来阐释两者的区别:

1、start() 方法

它的作用是启动一个新线程。

通过它启动的新线程,是处于就绪(可运行)状态的,此时还没有真正运行,等待 cpu 调度,一旦它得到 cpu 时间片,就会开始执行相应线程的 run() 方法了,这里 run() 方法称为线程体,它包含了要执行的这个线程的内容,run() 方法运行结束,此线程随即终止。

用 start() 方法来启动线程,真正实现了多线程运行,即无需等待某个线程的 run 方法体代码执行完毕就能够直接继续执行下面的代码,即进行线程切换。

2、run() 方法

就是一个普通的成员方法,没关系,你反复调用它都可以,反正它也不会帮你启动一个新线程。

程序中只有一个线程,即主线程,所以它的执行路径就只有一条,从上到下按顺序来执行,这就导致要等待 run 方法体执行完毕后,才可以继续执行下面的代码。也就是说没有实现多线程。

那怎么理解『多线程』这个概念?

举个例子,正值暑假,你家来了一波熊孩子,争着要玩你的 psp 游戏机,但你家 psp 就这么一台,为了让每个熊孩子都能玩上游戏机,你叫他们排起队,一人玩完接下一个人玩。那么 psp 就相当于 cpu,排队这个动作就是线程中的 start() 方法,等 cpu 选中一个熊孩子就轮到他,他就 run() 起来开始玩游戏,当 cpu 的运行时间片执行完,这个线程就继续排队,等待下一次的 run() 才能玩。

多线程其实就是分时利用CPU,宏观上让所有线程一起执行,殊不知,是 CPU 在做着极快速的上下文切换,给你营造出一种眼花缭乱的错觉,还真以为是同时执行的呢。

总结

好了,文章开头我们虽然“送走”了小哥,但作为一枚好心人,还是帮他总结回答下吧:

  1. start() 可以启动一个新线程,run() 不能
  2. start() 不能被重复调用,run() 可以
  3. start() 中的 run 代码可以不执行完就继续执行下面的代码,进行线程切换,而调用 run() 方法必须等待其代码全部执行完才能继续执行下面的代码
  4. start() 实现了多线程,run() 没有

转载于:https://www.cnblogs.com/jessehzx/p/11155457.html

<think>好的,我现在需要回答用户关于Java线程中start()run()方法的区别及使用场景的问题。首先,我得回忆一下自己之前学过的多线程知识,并结合提供的引用内容来组织答案。 首先,用户的问题明确,想要知道两者的区别以及使用场景。根据提供的引用,特别是引用[1]、[2]、[3][4],可以看出主要区别在于是否启动新线程。start()方法会创建新线程,而run()只是普通方法调用,会创建新线程。我需要明确这一点,并分点说明。 接下来,我应该从线程创建机制开始解释。start()方法会调用本地方法启动新线程,然后由JVM调用run(),而直接调用run()则是在当前线程执行。引用中的示例代码也能帮助说明这一点,比如引用[3]中的例子,调用start()会输出“线程运行中...”,而run()则是在主线程执行。 然后需要对比两者的区别,比如线程状态变化、执行方式、调用次数限制等。根据引用[2],调用start()后线程进入就绪状态,获得CPU时间片才会执行run(),而直接调用run()会改变线程状态,只是同步执行。同时,引用[4]提到start()只能调用一次,否则会抛出异常,而run()可以多次调用。 使用场景部分,需要强调何时使用start()来真正实现多线程,比如需要并发执行任务时。而run()可能用于测试或单线程环境中的方法调用。这里可以引用引用[4]中的总结,说明必须避免重复启动线程,而run()可以多次调用。 错误使用场景也需要提到,比如误用run()导致未启动新线程,或者多次调用start()导致异常。引用[5]中的例子显示直接调用run()可能达到预期效果,而引用[3]的示例对比了正确错误用法。 最后,生成相关问题,可能需要包括其他多线程相关的问题,比如线程生命周期、其他多线程实现方式等,但根据用户提供的示例,问题应围绕startrun方法的区别展开,如底层机制、异常处理、性能影响等。 现在需要确保回答结构清晰,分点说明区别,使用场景,并附上代码示例。同时,引用正确的来源,比如引用[1]-[5]中的内容,并在适当位置标注引用标记。还要注意LaTeX格式的正确使用,虽然这个问题可能需要数学公式,但如果有变量或代码块需要正确排版。 检查是否有遗漏点,比如线程状态的变化、异常情况、多次调用的限制等,确保覆盖所有关键区别。另外,是否需要提到同编程语言中的情况?用户的问题提到“Java或其他编程语言”,但引用内容都是关于Java的,所以可能主要讨论Java,其他语言可以简要提及或说明机制类似但实现可能同。 现在整理思路,组织成结构化的回答,使用标题分点,确保每个部分都有引用支持,并自然添加引用标识。同时,代码块需要用正确的格式,如Python中的示例,但这里讨论的是Java,需要提供Java代码示例,如引用[3]中的例子。 可能需要注意术语的一致性,比如“就绪状态”是否正确,或者是否有其他术语,根据引用[2]中的描述,确实使用“就绪(可运行)状态”,所以沿用这个说法。 最后,生成相关问题,参考用户示例中的问题,比如底层实现、异常处理、其他多线程方法比较等。</think>### Java线程中start()run()方法的区别及使用场景 #### 一、核心区别 1. **线程创建机制** - **`start()`**:通过调用`start()`方法,系统会创建新线程并触发`run()`方法的执行[^2]。此时线程进入**就绪状态**,等待CPU调度后执行`run()`内容[^2]。 - **`run()`**:直接调用`run()`方法仅是普通方法调用,会创建新线程,代码在当前线程(如主线程)中同步执行[^3]。 2. **线程状态变化** - `start()`会触发线程从**新建状态**转为**就绪状态**,最终进入**运行状态**。 - `run()`的调用会改变线程状态,仅作为方法执行。 #### 二、代码示例对比 ```java // 示例1:正确使用start()启动新线程 Thread thread = new Thread(() -> { System.out.println("新线程ID:" + Thread.currentThread().getId()); }); thread.start(); // 输出新线程ID(同于主线程) // 示例2:直接调用run()的误区 Thread thread2 = new Thread(() -> { System.out.println("当前线程ID:" + Thread.currentThread().getId()); }); thread2.run(); // 输出主线程ID(未创建新线程) ``` #### 三、关键差异总结 | 特性 | `start()` | `run()` | |---------------------|--------------------------------------|------------------------------| | **线程数量** | 创建新线程 | 创建新线程 | | **执行方式** | 异步执行(由JVM调度) | 同步执行(阻塞当前线程) | | **调用限制** | 同一线程对象只能调用一次,否则抛异常 | 可多次调用 | | **底层实现** | 调用本地方法`start0()`启动线程[^1] | 普通方法调用 | #### 四、使用场景 1. **`start()`适用场景** - 需要实现多任务并行时(如下载文件、实时数据处理)[^4]。 - 需要利用多核CPU资源提升性能的任务。 2. **`run()`适用场景** - **调试测试**:在单线程环境下验证`run()`逻辑。 - **同步执行**:明确要求代码在当前线程顺序执行时(极少使用)。 #### 五、常见误区与注意事项 1. **错误使用`run()`替代`start()`** ```java // 错误示例:误用run()导致未启动新线程 new Thread(task).run(); // 任务在主线程执行,失去多线程意义[^5] ``` 2. **重复调用`start()`** ```java Thread t = new Thread(task); t.start(); t.start(); // 抛出IllegalThreadStateException ``` 3. **线程生命周期管理** - 线程结束后(进入**终止状态**),可再次调用`start()`。 #### 六、其他编程语言的类比 - **Python**:通过`Thread.start()`启动线程,直接调用`run()`同样会创建新线程。 - **C++**:通过`std::thread`的`join()`或`detach()`管理线程执行。 --- §§ 相关问题 §§ 1. 为什么Java允许重复调用同一个线程对象的`start()`方法? 2. 如何通过线程池(如`ExecutorService`)优化多线程性能? 3. 线程的`wait()``sleep()`方法与`run()`的执行有何关联? 4. 在多线程环境下,如何避免资源竞争问题(如死锁)?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值