开发易忽视的问题:一个线程两次调用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
  • 原因:线程生命周期状态的限制,一个线程对象只能被启动一次。
  • 解决:如需重复执行任务,创建新的线程对象,或使用线程池(推荐)。
相关推荐
界面开发小八哥29 分钟前
Java开发工具IntelliJ IDEA v2025.1——全面支持Java 24、整合AI
java·ide·人工智能·intellij-idea·idea
BXCQ_xuan40 分钟前
基于Node.js的健身会员管理系统的后端开发实践
后端·mysql·node.js
普兰店拉马努金1 小时前
【高中数学/古典概率】4红2黑六选二,求取出两次都是红球的概率
java·概率
智商低情商凑1 小时前
CAS(Compare And Swap)
java·jvm·面试
yangmf20401 小时前
使用 Logstash 迁移 MongoDB 数据到 Easysearch
java·elasticsearch·搜索引擎
Tiger_shl1 小时前
【Python语言基础】24、并发编程
java·数据库·python
拉满buff搞代码1 小时前
搞定 PDF“膨胀”难题:Python + Java 的超实用压缩秘籍
后端
FAQEW1 小时前
Spring boot 中的IOC容器对Bean的管理
java·spring boot·后端·bean·ioc容器
<<1 小时前
基于Django的权限管理平台
后端·python·django
林夕11201 小时前
颠覆认知的MySQL全解析:安装、连接到SQL三大核心语句全掌握
后端·mysql