Java并发编程–3-start()和run()区别

Java并发编程——start() 和 run() 的区别(高频面试题)

这是并发基础中最经典、最容易被问到的一个点,几乎每场Java后端/并发面试都会涉及。很多人以为“调用run()就能启动线程”,其实这是个大误区。

1. 核心区别总结(背诵版)

维度start()run()
是否创建新线程(调用native方法,创建OS级线程)(就是一个普通的方法调用)
执行线程新线程中执行run()方法当前线程(调用者线程)中执行
调用次数只能调用一次,第二次抛 IllegalThreadStateException可以调用多次(普通方法)
并发效果真正实现多线程并发执行串行执行,无并发
线程状态变化NEW → RUNNABLE无状态变化(线程对象仍处于NEW或其它状态)
调用栈为新线程创建一个独立的调用栈使用当前线程的调用栈
本质启动线程的入口方法线程的任务逻辑(业务代码)

一句话总结
start() 是用来启动线程的,run() 是用来定义线程要做什么的。
直接调用 run() 只是把线程逻辑当成普通方法在当前线程里跑,完全达不到多线程的目的

2. 代码示例对比(强烈建议自己敲一遍)

public class StartVsRunDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("当前线程名称: " + Thread.currentThread().getName());
            System.out.println("我是通过 start() 启动的线程");
        }, "Thread-Start");

        Thread t2 = new Thread(() -> {
            System.out.println("当前线程名称: " + Thread.currentThread().getName());
            System.out.println("我是直接调用 run() 的");
        }, "Thread-Run");

        // 正确方式:使用 start()
        t1.start();

        // 错误方式:直接调用 run()
        t2.run();   // 注意这里是 run() 而不是 start()

        System.out.println("main线程结束");
    }
}

预期输出(顺序可能有细微差异)

当前线程名称: Thread-Start
我是通过 start() 启动的线程
当前线程名称: main          ← 注意这里是 main 线程!
我是直接调用 run() 的
main线程结束
  • t1.start() → 新线程执行,线程名是 Thread-Start
  • t2.run() → 在 main线程 中执行,线程名是 main

3. 底层原理简析(面试追问常考)

  • start() 方法做了什么?
  1. 检查线程状态(是否已经启动过)。
  2. 调用本地方法 start0()(JNI → JVM底层)。
  3. JVM为该线程分配资源、创建OS原生线程。
  4. 把线程状态改为 RUNNABLE
  5. 新线程启动后,JVM会自动调用该线程的 run() 方法。
  • run() 方法
  • 只是 Runnable 接口或 Thread 类中定义的一个普通实例方法
  • 如果你直接调用,它和调用 obj.someMethod() 没有任何区别。

4. 常见面试追问及答案

Q1:如果你直接调用 run(),线程还会进入 RUNNABLE 状态吗?
A:不会。线程对象状态不变,run() 里的代码在当前线程(通常是 main)中同步执行。

Q2:start() 可以调用两次吗?为什么?
A:不可以。第二次调用会抛 IllegalThreadStateException,因为线程状态已经不是 NEW 了(Thread 内部有状态检查)。

Q3:run() 可以调用多次吗?
A:可以,因为它只是普通方法。

Q4:为什么 Thread 类要同时实现 Runnable,并提供 run() 方法?
A:这是模板方法模式的经典应用。start() 是固定流程,run() 是可以被子类或 Runnable 覆盖的“钩子方法”(hook)。

Q5:实际开发中有人会故意直接调用 run() 吗?
A:极少。但在某些单元测试、或想在当前线程同步执行任务逻辑时可能会用(比如测试线程内代码),但生产环境几乎不用。

5. 最佳实践

  • 永远使用 start() 来启动线程,不要直接调用 run()
  • 推荐方式:实现 Runnable 接口 + new Thread(runnable).start(),或使用线程池提交任务。
  • 在现代Java中(JDK 8+),更推荐使用 ExecutorService / 线程池CompletableFuture,避免手动管理线程。

掌握了 start() vs run(),你就真正理解了“线程的创建和执行是两回事”这个并发核心思想。

这是并发基础系列的补充,如果你想继续看:

  • 线程生命周期(5/6种状态 + 状态转换图)
  • synchronized 底层与锁升级
  • volatile 原理与内存屏障
  • Happens-Before 规则详解

随时说“下一题”或具体问某个点,我继续给你画图+代码+面试答案。

有疑问或想看完整可运行的Demo代码,告诉我!

文章已创建 5130

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部