开发易忽视的问题:一个线程两次调用start()方法

在 Java 中,如果对同一个线程对象多次调用 start() 方法,会引发 java.lang.IllegalThreadStateException 异常。这是因为 start() 方法的设计规定,一个线程只能启动一次。


原理分析

  1. 线程的生命周期

    • 一个线程从创建到终止,经历以下几种状态:

      • NEW(新建) :线程对象被创建,但还未调用 start() 方法。
      • RUNNABLE(可运行) :调用 start() 方法后,线程进入就绪状态,等待 CPU 调度。
      • TERMINATED(终止) :线程运行结束或异常退出后,进入终止状态。
  2. start() 方法的作用

    • start() 方法的功能是启动线程,将线程从 NEW 状态变为 RUNNABLE 状态。
    • 内部通过调用 JVM 的本地方法 start0() 来启动线程。
    • 如果线程已经不是 NEW 状态,再次调用 start() 会导致异常。
  3. 为什么禁止多次调用 start()

    • start() 方法仅能将线程从 NEW 状态变为 RUNNABLE
    • 如果允许多次调用 start(),线程生命周期的管理将变得复杂,尤其是在已经执行过的线程上重复调用时。

示例代码

java 复制代码
public class ThreadStartExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Thread is running...");
        });

        thread.start(); // 第一次调用,线程启动成功
        thread.start(); // 第二次调用,抛出 IllegalThreadStateException 异常
    }
}

输出

plaintext 复制代码
Thread is running...
Exception in thread "main" java.lang.IllegalThreadStateException
    at java.base/java.lang.Thread.start(Thread.java:834)
    at ThreadStartExample.main(ThreadStartExample.java:10)

异常根本原因

  • Thread 类的 start() 方法实现中,有一个状态检查:

    java 复制代码
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    • threadStatus 是线程的内部状态标志,只有在 NEW 状态下,threadStatus 为 0。
    • 如果线程已经启动过(RUNNABLETERMINATED 等状态),再次调用 start() 时状态不为 0,抛出异常。

start() 方法源码

以下是 JDK 8 中 Thread 类的 start() 方法核心源码:

java 复制代码
public synchronized void start() {
    if (threadStatus != 0)  // 检查线程状态
        throw new IllegalThreadStateException();

    group.add(this);  // 将线程添加到线程组中

    boolean started = false;
    try {
        start0();  // 调用本地方法启动线程
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);  // 启动失败时移除线程
            }
        } catch (Throwable ignore) {
        }
    }
}

start0() 的底层实现

start0() 是一个本地方法,其底层逻辑由 JVM 的 C/C++ 代码实现。以下是 start0() 的部分行为:

3.1 线程创建(os::create_thread

  • 调用操作系统 API 创建原生线程。

  • 不同操作系统的实现有所不同:

    • 在 Linux 系统中,使用 pthread_create
    • 在 Windows 系统中,使用 _beginthreadex

3.2 线程栈初始化

  • 为新线程分配栈内存空间,确保运行时需要的资源就绪。
  • 栈空间大小由 JVM 参数(如 -Xss)控制。

3.3 调度线程执行

  • 将新创建的线程交给操作系统进行调度。
  • 当操作系统分配 CPU 时间片时,线程的 run() 方法会开始执行。

总结

  • 多次调用 start() :不被允许,会抛出 IllegalThreadStateException
  • 原因:线程生命周期状态的限制,一个线程对象只能被启动一次。
  • 解决:如需重复执行任务,创建新的线程对象,或使用线程池(推荐)。
相关推荐
非凡ghost7 分钟前
HWiNFO(专业系统信息检测工具)
前端·javascript·后端
非凡ghost9 分钟前
FireAlpaca(免费数字绘图软件)
前端·javascript·后端
ekkcole11 分钟前
java把word转pdf使用jar包maven依赖
java·pdf·word
非凡ghost16 分钟前
Sucrose Wallpaper Engine(动态壁纸管理工具)
前端·javascript·后端
Java小王子呀19 分钟前
Java实现Excel转PDF
java·pdf·excel
间彧19 分钟前
从零到一搭建Spring Cloud Alibbaba项目
后端
楼田莉子21 分钟前
C++学习:C++11关于类型的处理
开发语言·c++·后端·学习
LSTM9724 分钟前
使用 Java 对 PDF 添加水印:提升文档安全与版权保护
后端
该用户已不存在24 分钟前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程
用户2986985301427 分钟前
Spire.Doc 实践指南:将Word 文档转换为 XML
后端·.net