杰瑞科技汇

Java线程start()和run()到底有啥区别?

核心结论(一句话总结)

  • start(): 启动一个新线程,并让这个新线程去执行 run() 方法中的代码。只能调用一次
  • run(): 只是一个普通的方法,如果直接调用它,它会在当前调用线程中执行,不会启动新的线程。可以被多次调用

详细解释与对比

为了让你彻底理解,我们从多个维度来对比它们。

Java线程start()和run()到底有啥区别?-图1
(图片来源网络,侵删)

start() 方法

当你创建一个 Thread 对象后,调用 start() 方法,会发生以下事情:

  1. 创建新线程:JVM(Java虚拟机)会为这个线程分配一个新的栈空间(Stack),这个栈空间是独立于主线程的。
  2. 线程状态变更:该线程的状态会从 NEW(新建)变为 RUNNABLE(可运行)。
  3. 执行权交给调度器:该线程进入就绪队列,等待CPU的调度,一旦CPU分配时间片,JVM就会从这个新线程的栈中开始执行 run() 方法的代码。
  4. 异步执行start() 方法本身会立即返回,而不会等待 run() 方法执行完毕,主线程会继续向下执行,而新线程在后台并发执行,这就是所谓的“异步”。

示例代码:

class MyThread extends Thread {
    @Override
    public void run() {
        // 这段代码将在新线程中执行
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程正在运行: " + i);
            try {
                Thread.sleep(100); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class StartExample {
    public static void main(String[] args) {
        System.out.println("主线程开始运行...");
        MyThread myThread = new MyThread();
        myThread.start(); // 启动新线程
        // 主线程继续执行自己的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程正在运行: " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("主线程运行结束。");
    }
}

可能的输出结果(因为线程调度是随机的,所以顺序可能略有不同):

主线程开始运行...
主线程正在运行: 0
子线程正在运行: 0
主线程正在运行: 1
子线程正在运行: 1
主线程正在运行: 2
子线程正在运行: 2
主线程正在运行: 3
子线程正在运行: 3
主线程正在运行: 4
子线程正在运行: 4
主线程运行结束。

从输出可以看到,主线程和子线程是交替执行的,这证明了它们是两个独立的执行流。

Java线程start()和run()到底有啥区别?-图2
(图片来源网络,侵删)

run() 方法

run() 方法是 Thread 类中的一个普通方法,它实现了 Runnable 接口中的 run() 方法。

  • 直接调用:如果你直接在代码中调用 thread.run(),它不会创建新线程,它只是在当前正在执行的线程(比如主线程)中,像调用任何其他普通方法一样,按顺序执行 run() 方法里的代码,整个过程是同步的。

示例代码:

class MyTask implements Runnable { // 使用实现接口的方式更常见
    @Override
    public void run() {
        // 这段代码将在当前调用线程中执行
        for (int i = 0; i < 5; i++) {
            System.out.println("任务正在运行: " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class RunExample {
    public static void main(String[] args) {
        System.out.println("主线程开始运行...");
        MyTask myTask = new MyTask();
        Thread myThread = new Thread(myTask);
        // 直接调用 run(),没有启动新线程
        myThread.run(); 
        // 主线程会等待 run() 方法执行完毕后,才会继续执行下面的代码
        System.out.println("主线程运行结束。"); // 这行代码会在 run() 执行完后才打印
    }
}

可能的输出结果:

主线程开始运行...
任务正在运行: 0
任务正在运行: 1
任务正在运行: 2
任务正在运行: 3
任务正在运行: 4
主线程运行结束。

从输出可以看到,run() 方法执行完毕后,主线程才继续执行并打印 "主线程运行结束。",这证明了它是同步执行的。

Java线程start()和run()到底有啥区别?-图3
(图片来源网络,侵删)

对比表格

特性 start() run()
功能 启动一个新线程,并执行该线程的 run() 方法。 仅作为普通方法执行,内容写在方法体中。
线程创建 创建一个新的线程。 不会创建新线程。
执行上下文 新线程的栈空间中执行 run() 当前调用线程的栈空间中执行。
执行方式 异步,调用后立即返回,新线程与主线程并发执行。 同步,调用后会阻塞当前线程,直到 run() 方法执行完毕。
调用次数 一个 Thread 对象只能调用一次,多次调用会抛出 IllegalThreadStateException 可以被多次调用,就像任何普通方法一样。
本质 是一个native方法,与JVM底层线程机制交互。 是一个普通的Java实例方法

常见错误与陷阱

错误示例:混淆 start()run()

public class CommonMistake {
    public static void main(String[] args) {
        System.out.println("主线程开始...");
        Thread t = new Thread(() -> {
            System.out.println("这是在新线程中吗?");
        });
        // 错误:直接调用 run,没有启动新线程
        t.run(); 
        System.out.println("主线程结束。");
    }
}

输出:

主线程开始...
这是在新线程中吗?
主线程结束。

很多初学者会期望看到类似 start() 的并发输出,但实际上因为错误地调用了 run(),所有代码都在主线程中顺序执行了,失去了多线程的意义。

最佳实践

在现代Java开发中,更推荐使用 实现 Runnable 接口使用 Executor 框架 来创建和管理线程,而不是直接继承 Thread 类,这样可以更好地解耦任务和线程本身。

Runnable 接口示例:

// 1. 定义任务
class MyRunnableTask implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
    }
}
// 2. 创建线程并启动
public class RunnableExample {
    public static void main(String[] args) {
        MyRunnableTask task = new MyRunnableTask();
        Thread thread = new Thread(task, "我的自定义线程"); // 给线程起个名字
        thread.start(); // 这里还是调用 start()
    }
}

记住这个简单的类比:

  • start() 就像是你按下了汽车的启动按钮,你按下按钮后,引擎(新线程)开始工作,而你(主线程)可以立即松开按钮去做别的事情(比如系安全带)。
  • run() 就像是你用手转动曲轴来启动引擎,你必须亲自、持续地转动,引擎才会工作,期间你不能去做别的事情。

理解 start()run() 的区别是掌握Java多线程编程的第一步,也是最重要的一步。

分享:
扫描分享到社交APP
上一篇
下一篇