当前位置: 首页 > news >正文

如果一个线程在 Java 中被两次调用 start() 方法,会发生什么?

Java 线程两次调用 start() 的后果

直接答案

如果一个线程在 Java 中被两次调用start()方法,第二次调用会抛出IllegalThreadStateException异常

详细说明

1. 异常示例

publicclassThreadStartExample{publicstaticvoidmain(String[]args){Threadthread=newThread(()->{System.out.println("Thread is running");});// 第一次调用 - 正常thread.start();// 第二次调用 - 抛出异常thread.start();// IllegalThreadStateException}}

输出结果:

Thread is running Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.start(Thread.java:708) at ThreadStartExample.main(ThreadStartExample.java:12)

2. 为什么会抛出异常?

这是由 Java 线程的生命周期机制决定的:

NEW (新建) ↓ start() RUNNABLE (可运行) ↓ TERMINATED (终止)
  • NEW 状态:线程对象创建但未启动
  • RUNNABLE 状态:线程正在运行或等待 CPU
  • TERMINATED 状态:线程执行完毕

start()方法只能从NEW 状态调用一次,调用后线程状态变为 RUNNABLE,再次调用时状态已经不是 NEW,因此抛出异常。

3. Thread.start() 源码分析

// Thread 类的部分源码(简化版)publicclassThreadimplementsRunnable{privatevolatileintthreadStatus;// 线程状态publicsynchronizedvoidstart(){// 检查线程状态,如果不是 0(NEW 状态),抛出异常if(threadStatus!=0)thrownewIllegalThreadStateException();// 通知线程组该线程即将启动group.add(this);booleanstarted=false;try{// 调用本地方法创建并启动线程start0();started=true;}finally{try{if(!started){group.threadStartFailed(this);}}catch(Throwableignore){// 不处理异常}}}// 本地方法,由 JVM 实现privatenativevoidstart0();}

4. 线程状态转换图

start() ┌─────────┐ ┌──────────┐ │ NEW │ ──────────► │ RUNNABLE │ └─────────┘ └──────────┘ │ │ run() 完成 ▼ ┌──────────────┐ │ TERMINATED │ └──────────────┘ 再次调用 start() → IllegalThreadStateException

正确的使用方式

1. 创建新线程对象

publicclassCorrectThreadUsage{publicstaticvoidmain(String[]args){// 第一次执行Threadthread1=newThread(()->{System.out.println("Thread 1 is running");});thread1.start();// 如果需要再次执行,创建新的线程对象Threadthread2=newThread(()->{System.out.println("Thread 2 is running");});thread2.start();}}

2. 使用线程池

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;publicclassThreadPoolExample{publicstaticvoidmain(String[]args){ExecutorServiceexecutor=Executors.newFixedThreadPool(2);Runnabletask=()->{System.out.println("Task is running by: "+Thread.currentThread().getName());};// 可以多次提交同一个任务executor.submit(task);executor.submit(task);executor.submit(task);executor.shutdown();}}

3. 使用 Runnable 接口

publicclassRunnableExample{publicstaticvoidmain(String[]args){Runnabletask=()->{System.out.println("Task running");};// 可以创建多个线程执行同一个任务newThread(task).start();newThread(task).start();newThread(task).start();}}

检查线程状态

1. 使用 getState() 方法

publicclassThreadStateCheck{publicstaticvoidmain(String[]args){Threadthread=newThread(()->{System.out.println("Thread is running");});System.out.println("Before start: "+thread.getState());// NEWthread.start();System.out.println("After start: "+thread.getState());// RUNNABLEtry{thread.join();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("After completion: "+thread.getState());// TERMINATED}}

2. 使用 isAlive() 方法

publicclassThreadAliveCheck{publicstaticvoidmain(String[]args){Threadthread=newThread(()->{try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}});System.out.println("Is alive before start: "+thread.isAlive());// falsethread.start();System.out.println("Is alive after start: "+thread.isAlive());// truetry{thread.join();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Is alive after completion: "+thread.isAlive());// false}}

常见错误场景

1. 错误:重用已启动的线程

// ❌ 错误做法Threadthread=newThread(task);thread.start();// ... 一些操作thread.start();// IllegalThreadStateException

2. 正确:创建新线程

// ✅ 正确做法Threadthread1=newThread(task);thread1.start();// ... 一些操作Threadthread2=newThread(task);thread2.start();

3. 错误:在循环中重用线程

// ❌ 错误做法Threadthread=newThread(task);for(inti=0;i<5;i++){thread.start();// 第二次开始抛出异常}

4. 正确:在循环中创建新线程

// ✅ 正确做法for(inti=0;i<5;i++){Threadthread=newThread(task);thread.start();}

总结

要点说明
异常类型IllegalThreadStateException
异常时机第二次调用start()方法时
原因线程状态已从 NEW 变为 RUNNABLE 或 TERMINATED
解决方案创建新的 Thread 对象或使用线程池
检查方法getState()isAlive()

核心原则:每个 Thread 对象只能启动一次,如果需要多次执行相同任务,应该创建新的 Thread 对象或使用线程池。

http://www.jsqmd.com/news/403309/

相关文章:

  • 图论——最短路Dijkstra算法
  • 2026年保健品推荐:品质与口碑并存,养胃颗粒/保健饮品/保健品,保健品品牌有哪些 - 品牌推荐师
  • [NOI2018] 冒泡排序
  • 通过MATLAB控制COMSOL Multiphysisc仿真进程模拟局部放电,建立有限元仿真模型
  • 【GLM-5 陪练式前端新手入门】第四篇:卡片布局 —— 让个人主页内容更有层次
  • Splay进阶
  • 【GLM-5 陪练式前端新手入门】第三篇:网页导航栏 —— 搭建个人主页的 “指路牌”
  • [AI提效-17]-豆包图片生成功能新手入门指南
  • 写一个自动检测照片光线构图,给出优化建议,颠覆拍照全靠盲拍。
  • Python基于Vue的 古城景区管理系统的设计与实现django flask pycharm
  • 视频孪生平台之上:镜像视界三维实时解算体系在危化园区风险半径动态解算中的全球领先性研究
  • 2134523
  • 5784784
  • 深度解读:Android开发工程师岗位核心能力与技术进阶之路——以苏州池久节能电气有限公司职位要求为例
  • 苏州智观易盛信息科技有限公司 Android 开发工程师职位深度解析与面试全攻略
  • AI 2.0提示工程架构师:提示词调试与优化的9个实用工具
  • 大数据领域日志数据压缩算法的比较与选择
  • Zookeeper为大数据领域分布式计算带来的优势
  • 解决推荐同质化!Agentic AI提示工程在个性化推荐系统中的创新应用
  • 顶极模型大比拼,到底谁才是真正的编程之王?
  • AI应用架构师与科研数据AI分析工具的协同作战
  • 0222cursor日志
  • 大数据领域分布式存储的扩展性设计思路
  • Python json write serialized content to json file
  • 【脑洞编程】从“治国理政”看懂C++广播机制:全局变量的“中央集权”与“局部自治”
  • 设计一个电商平台的购物车系统。
  • C++ 多线程与并发系统取向(二)—— 资源保护:std::mutex 与 RAII(类比 Java synchronized)
  • 深入理解 Java join:它到底做了什么?和协程挂起有什么区别?
  • Java 版 Claude Code CLI 来了!(国产开源)Solon Code CLI 发布
  • [AI提效-15]-豆包多对话功能详解:打破传统AI工具“单对话单一主题、新对话从零起步”的局限,高效衔接创作,告别反复沟通成本。