开发易忽视的问题:一个线程两次调用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
  • 原因:线程生命周期状态的限制,一个线程对象只能被启动一次。
  • 解决:如需重复执行任务,创建新的线程对象,或使用线程池(推荐)。
相关推荐
s_fox_11 分钟前
Nginx Embedded Variables 嵌入式变量解析(4)
java·网络·nginx
一个 00 后的码农14 分钟前
25旅游管理研究生复试面试问题汇总 旅游管理专业知识问题很全! 旅游管理复试全流程攻略 旅游管理考研复试真题汇总
考研·面试·面试问题·考研复试·旅游管理·复试调剂·面试真题
Jelena1577958579217 分钟前
使用Java爬虫获取1688 item_get_company 接口的公司档案信息
java·开发语言·爬虫
数据小小爬虫20 分钟前
Jsoup解析商品详情时,如何确保数据准确性?
java·爬虫
V+zmm1013430 分钟前
自驾游拼团小程序的设计与实现(ssm论文源码调试讲解)
java·数据库·微信小程序·小程序·毕业设计
坚定信念,勇往无前42 分钟前
springboot单机支持1w并发,需要做哪些优化
java·spring boot·后端
丁总学Java1 小时前
`AdminAdminDTO` 和 `userSession` 对象中的字段对应起来的表格
java
老友@1 小时前
OnlyOffice:前端编辑器与后端API实现高效办公
前端·后端·websocket·编辑器·onlyoffice
m0_748240252 小时前
SpringMVC详解
java
剑走偏锋o.O2 小时前
Java四大框架深度剖析:MyBatis、Spring、SpringMVC与SpringBoot
java·spring boot·spring·mybatis