深入拆解Java线程:生命周期流转与核心方法底层原理

简介: Java线程是并发编程核心,其生命周期含NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED六种状态,配合interrupt()、wait()/notify()、join()等方法实现协作与控制,掌握状态转换与原理是编写高效稳定并发程序的基础。

线程是Java并发编程的核心执行单元,理解其生命周期与状态转换机制,以及interrupt()、wait()、notify()、join()等核心方法的底层原理,是编写高效、稳定并发程序的基础。

一、Java线程的生命周期与状态转换

Java线程的状态由java.lang.Thread.State枚举定义,共包含6种状态,这些状态在特定条件下会相互转换,形成完整的生命周期流转。

1.1 线程状态详解

NEW(新建)

线程对象已创建,但尚未调用start()方法启动。此时线程仅在JVM内存中存在,未分配CPU时间片,也未开始执行。

RUNNABLE(可运行)

线程已调用start()方法启动,处于可执行状态。该状态包含两种子状态:

  • Ready:线程已准备好,等待CPU时间片调度
  • Running:线程正在CPU上执行

Java将这两种子状态统一称为RUNNABLE,因为线程在Ready和Running之间的切换由JVM线程调度器控制,应用程序无法直接感知。

BLOCKED(阻塞)

线程因等待监视器锁(synchronized)而被阻塞。当线程尝试进入被其他线程持有的synchronized块/方法时,会进入该状态,直到获取到锁才会转换回RUNNABLE。

WAITING(无限期等待)

线程进入无限期等待状态,需等待其他线程执行特定操作(如notify()/notifyAll())才能被唤醒。进入该状态的场景包括:

  • 调用无超时参数的Object.wait()
  • 调用无超时参数的Thread.join()
  • 调用LockSupport.park()

TIMED_WAITING(有时限等待)

线程进入有时限的等待状态,超过指定时间后会自动唤醒,或在等待时间内被其他线程唤醒。进入该状态的场景包括:

  • 调用Thread.sleep(long millis)
  • 调用带超时参数的Object.wait(long timeout)
  • 调用带超时参数的Thread.join(long millis)
  • 调用LockSupport.parkNanos(long nanos)
  • 调用LockSupport.parkUntil(long deadline)

TERMINATED(终止)

线程执行完毕,生命周期结束。线程进入该状态的原因包括:

  • run()方法正常执行完毕
  • 线程执行过程中抛出未捕获的异常

1.2 状态转换全流程

二、核心方法的底层原理与正确使用场景

2.1 interrupt():线程中断机制

底层原理

interrupt()的核心作用是设置线程的中断标志位,而非强制终止线程。线程需要主动检查中断标志位并响应中断,这是一种协作式的中断机制。

相关方法说明:

  • void interrupt():设置线程的中断标志位为true
  • boolean isInterrupted():检查线程的中断标志位,不清除标志位
  • static boolean interrupted():检查当前线程的中断标志位,清除标志位(将其重置为false)

当线程处于WAITING或TIMED_WAITING状态时,调用interrupt()会抛出InterruptedException,并清除中断标志位,因此在捕获该异常后,通常需要重新设置中断标志位,以便上层逻辑感知。

正确使用场景

interrupt()主要用于优雅地停止线程,避免使用已废弃的stop()方法(该方法会强制终止线程,导致资源未释放、数据不一致等问题)。

代码实例

package com.jam.demo;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class InterruptDemo implements Runnable {
   @Override
   public void run() {
       while (!Thread.currentThread().isInterrupted()) {
           try {
               Thread.sleep(1000);
               log.info("线程正在执行任务");
           } catch (InterruptedException e) {
               log.info("捕获到InterruptedException,重置中断标志位");
               Thread.currentThread().interrupt();
           }
       }
       log.info("线程响应中断,结束运行");
   }
   public static void main(String[] args) throws InterruptedException {
       Thread thread = new Thread(new InterruptDemo(), "ken-interrupt-thread");
       thread.start();
       Thread.sleep(3500);
       log.info("主线程调用interrupt()中断子线程");
       thread.interrupt();
   }
}

2.2 wait()、notify()、notifyAll():线程协作

底层原理

这三个方法是java.lang.Object类的native方法,需与synchronized关键字配合使用,核心是基于对象的监视器(Monitor)实现线程间的等待/通知机制。

  • Monitor结构:每个对象都关联一个Monitor,包含两个队列:
  • Entry Set(入口集):存放等待获取对象锁的线程(对应BLOCKED状态)
  • Wait Set(等待集):存放调用wait()后进入等待的线程(对应WAITING/TIMED_WAITING状态)
  • wait()原理
  1. 释放当前持有的对象锁
  2. 将当前线程加入Wait Set
  3. 线程进入WAITING/TIMED_WAITING状态
  • notify()原理
  1. 从Wait Set中随机选择一个线程唤醒
  2. 被唤醒的线程从Wait Set移动到Entry Set
  3. 被唤醒的线程等待获取对象锁,获取后进入RUNNABLE状态
  • notifyAll()原理
  1. 唤醒Wait Set中的所有线程
  2. 所有被唤醒的线程从Wait Set移动到Entry Set
  3. 这些线程竞争对象锁,获取到锁的线程进入RUNNABLE状态

注意事项

  1. 必须在synchronized块/方法中调用:否则会抛出IllegalMonitorStateException
  2. 循环检查条件:避免虚假唤醒(Spurious Wakeups),即线程可能在没有被notify()/notifyAll()的情况下自动唤醒
  3. 优先使用notifyAll() :notify()可能导致信号丢失,notifyAll()能确保所有等待线程都有机会被唤醒

正确使用场景

主要用于实现线程间的协作,如生产者-消费者模式、Future任务实现等。

代码实例:生产者-消费者模式

package com.jam.demo;
import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
import java.util.Queue;
@Slf4j
public class ProducerConsumerDemo {
   private static final int QUEUE_CAPACITY = 5;
   private final Queue<Integer> queue = new LinkedList<>();
   public void produce() throws InterruptedException {
       synchronized (queue) {
           while (queue.size() == QUEUE_CAPACITY) {
               log.info("队列已满,生产者线程等待");
               queue.wait();
           }
           int product = (int) (Math.random() * 100);
           queue.offer(product);
           log.info("生产者生产产品:{},当前队列大小:{}", product, queue.size());
           queue.notifyAll();
       }
   }
   public void consume() throws InterruptedException {
       synchronized (queue) {
           while (queue.isEmpty()) {
               log.info("队列为空,消费者线程等待");
               queue.wait();
           }
           int product = queue.poll();
           log.info("消费者消费产品:{},当前队列大小:{}", product, queue.size());
           queue.notifyAll();
       }
   }
   public static void main(String[] args) {
       ProducerConsumerDemo demo = new ProducerConsumerDemo();
       Thread producer = new Thread(() -> {
           try {
               for (int i = 0; i < 10; i++) {
                   demo.produce();
                   Thread.sleep(400);
               }
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
           }
       }, "ken-producer");
       Thread consumer1 = new Thread(() -> {
           try {
               for (int i = 0; i < 5; i++) {
                   demo.consume();
                   Thread.sleep(700);
               }
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
           }
       }, "ken-consumer-1");
       Thread consumer2 = new Thread(() -> {
           try {
               for (int i = 0; i < 5; i++) {
                   demo.consume();
                   Thread.sleep(900);
               }
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
           }
       }, "ken-consumer-2");
       producer.start();
       consumer1.start();
       consumer2.start();
   }
}

2.3 join():线程等待

底层原理

join()的底层是通过调用wait()实现的,让当前线程等待目标线程执行完毕。当目标线程执行完毕后,JVM会自动调用notifyAll()唤醒所有等待的线程。

核心逻辑(简化版):

public final synchronized void join(long millis) throws InterruptedException {
   long base = System.currentTimeMillis();
   long now = 0;
   if (millis < 0) {
       throw new IllegalArgumentException("timeout value is negative");
   }
   if (millis == 0) {
       while (isAlive()) {
           wait(0);
       }
   } else {
       while (isAlive()) {
           long delay = millis - now;
           if (delay <= 0) {
               break;
           }
           wait(delay);
           now = System.currentTimeMillis() - base;
       }
   }
}

正确使用场景

主要用于等待一个或多个线程执行完毕后再继续执行当前线程,如并行任务的结果汇总。

代码实例

package com.jam.demo;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JoinDemo {
   public static void main(String[] args) throws InterruptedException {
       Thread task1 = new Thread(() -> {
           try {
               log.info("任务1开始执行");
               Thread.sleep(2000);
               log.info("任务1执行完毕");
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
           }
       }, "ken-task-1");
       Thread task2 = new Thread(() -> {
           try {
               log.info("任务2开始执行");
               Thread.sleep(3000);
               log.info("任务2执行完毕");
           } catch (InterruptedException e) {
               Thread.currentThread().interrupt();
           }
       }, "ken-task-2");
       task1.start();
       task2.start();
       log.info("主线程等待任务1和任务2执行完毕");
       task1.join();
       task2.join();
       log.info("所有任务执行完毕,主线程继续执行");
   }
}

三、易混淆技术点区分

3.1 wait() vs sleep()

特性 wait() sleep()
所属类 Object Thread
锁释放 释放当前持有的对象锁 不释放锁
使用场景 线程间协作 暂停当前线程执行
唤醒条件 需被notify()/notifyAll()唤醒,或超时 超时时间到,或被interrupt()
异常声明 抛出InterruptedException 抛出InterruptedException

3.2 notify() vs notifyAll()

  • notify() :只唤醒Wait Set中的一个线程,选择策略由JVM实现决定(通常是随机或FIFO)
  • notifyAll() :唤醒Wait Set中的所有线程,这些线程会竞争对象锁

在大多数场景下,建议使用notifyAll(),避免信号丢失问题。

3.3 isInterrupted() vs interrupted()

  • isInterrupted() :实例方法,检查调用该方法的线程的中断标志位,不清除标志位
  • interrupted():静态方法,检查当前线程的中断标志位,清除标志位(重置为false)

总结

理解Java线程的生命周期与状态转换,以及interrupt()、wait()、notify()、join()等核心方法的底层原理,是编写高质量并发程序的关键。

目录
相关文章
|
10天前
|
人工智能 数据可视化 安全
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
本文详解如何用阿里云Lighthouse一键部署OpenClaw,结合飞书CLI等工具,让AI真正“动手”——自动群发、生成科研日报、整理知识库。核心理念:未来软件应为AI而生,CLI即AI的“手脚”,实现高效、安全、可控的智能自动化。
34600 28
王炸组合!阿里云 OpenClaw X 飞书 CLI,开启 Agent 基建狂潮!(附带免费使用6个月服务器)
|
3天前
|
人工智能 机器人 开发工具
Windows 也能跑 Hermes Agent!完整安装教程 + 飞书接入,全程避坑
Hermes Agent 是一款自学习AI智能体系统,支持一键安装与飞书深度集成。本教程详解Windows下从零部署全流程,涵盖依赖自动安装、模型配置、飞书机器人接入及四大典型兼容性问题修复,助你快速构建企业级AI协作平台。(239字)
4463 10
|
5天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
4745 19
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
22天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
45455 151
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
12天前
|
人工智能 JSON 监控
Claude Code 源码泄露:一份价值亿元的 AI 工程公开课
我以为顶级 AI 产品的护城河是模型。读完这 51.2 万行泄露的源码,我发现自己错了。
5228 21
|
4天前
|
机器学习/深度学习 存储 人工智能
还在手写Skill?hermes-agent 让 Agent 自己进化能力
Hermes-agent 是 GitHub 23k+ Star 的开源项目,突破传统 Agent 依赖人工编写Aegnt Skill 的瓶颈,首创“自我进化”机制:通过失败→反思→自动生成技能→持续优化的闭环,让 Agent 在实践中自主构建、更新技能库,持续自我改进。
1044 3

热门文章

最新文章

下一篇
开通oss服务