杰瑞科技汇

java 线程 start run

核心结论一句话总结

  • start(): 启动一个新线程,JVM 会在该线程中自动调用 run() 方法,实现了真正的多线程。
  • run(): 只是一个普通的方法,如果直接调用它,它会在当前线程中执行,不会启动新的线程,是单线程行为。

详细解释与代码示例

为了更好地理解,我们通过一个具体的例子来看。

java 线程 start run-图1
(图片来源网络,侵删)

start() 方法的作用

当你调用一个线程对象的 start() 方法时,会发生两件事:

  1. 创建一个新的线程:JVM 会为这个线程分配独立的内存空间、程序计数器、栈等资源。
  2. 调用 run() 方法:这个新创建的线程会开始执行其 run() 方法中的代码。

关键点run() 方法是在新线程的上下文中执行的,因此它与调用 start() 的线程是并发执行的。

run() 方法的本质

run() 方法是 Thread 类中的一个普通实例方法,它的定义如下:

public void run() {
    if (target != null) {
        target.run();
    }
}

如果你直接调用 myThread.run()run() 方法里的代码会像调用任何其他普通方法一样,在当前正在执行的线程中顺序执行,并不会创建新的线程。

java 线程 start run-图2
(图片来源网络,侵删)

代码对比示例

下面这个例子将清晰地展示二者的区别。

class MyTask implements Runnable {
    @Override
    public void run() {
        // 获取当前正在执行此代码的线程对象
        Thread currentThread = Thread.currentThread();
        System.out.println("run() 方法开始执行,线程名: " + currentThread.getName() + ", ID: " + currentThread.getId());
        try {
            // 模拟耗时任务
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("run() 方法执行完毕,线程名: " + currentThread.getName() + ", ID: " + currentThread.getId());
    }
}
public class StartVsRunDemo {
    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        Thread myThread = new Thread(myTask);
        System.out.println("主线程开始,线程名: " + Thread.currentThread().getName());
        // --- 情况一:调用 start() ---
        System.out.println("\n--- 调用 start() ---");
        myThread.start(); // 启动新线程
        // --- 情况二:调用 run() ---
        System.out.println("\n--- 调用 run() ---");
        // myThread.run(); // 直接调用 run() 方法
        System.out.println("主线程继续执行,线程名: " + Thread.currentThread().getName());
    }
}

运行结果分析

取消注释 myThread.start();,注释掉 myThread.run();

主线程开始,线程名: main
--- 调用 start() ---
主线程继续执行,线程名: main
run() 方法开始执行,线程名: Thread-0, ID: 13
run() 方法执行完毕,线程名: Thread-0, ID: 13

分析:

  1. 主线程(main 线程)启动后,执行到 myThread.start()
  2. start() 方法启动了一个新的线程,我们称之为 "Thread-0"。
  3. 关键点:主线程和 "Thread-0" 是并发执行的,所以主线程不会等待 "Thread-0" 执行完,而是继续往下打印 "主线程继续执行..."。
  4. "Thread-0" 线程在稍后(可能是几毫秒后)开始执行 run() 方法中的代码,你可以看到 run() 方法中的 Thread.currentThread().getName() 返回的是 "Thread-0",而不是 "main"。

取消注释 myThread.run();,注释掉 myThread.start();

java 线程 start run-图3
(图片来源网络,侵删)
主线程开始,线程名: main
--- 调用 run() ---
run() 方法开始执行,线程名: main, ID: 1
run() 方法执行完毕,线程名: main, ID: 1
主线程继续执行,线程名: main

分析:

  1. 主线程执行到 myThread.run()
  2. 这只是一个普通的方法调用,run() 方法中的代码在主线程中直接、顺序地执行。
  3. 主线程会阻塞,直到 run() 方法执行完毕后,才会继续执行下面的 System.out.println("主线程继续执行...");
  4. 你可以看到 run() 方法中的 Thread.currentThread().getName() 返回的是 "main",证明它是在主线程中运行的。

总结表格

特性 thread.start() thread.run()
作用 启动一个新的线程,并让该线程执行 run() 方法。 直接调用 run() 方法本身。
线程创建 ,会创建一个新的执行线程。 ,没有创建新线程。
执行上下文 run() 方法中的代码在新创建的线程中执行。 run() 方法中的代码在调用它的当前线程中执行。
并发性 实现了多线程,主线程和新线程可以并发执行。 没有实现多线程,代码是顺序执行的。
调用方式 只能通过 start() 方法来触发新线程的执行。 run() 只是一个普通方法,可以像任何其他方法一样被直接调用。
典型错误 - 误以为 myThread.run() 会启动一个新线程来执行任务。

最佳实践

当你希望一段代码在后台异步、并发地执行时,你应该:

  1. 创建一个 Runnable 对象(或者继承 Thread 类,但通常不推荐)。
  2. 将这个 Runnable 对象包装在一个 Thread 对象中。
  3. 调用这个 Thread 对象的 start() 方法。

永远不要直接调用 run() 方法来期望实现多线程效果。

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