深入拆解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()等核心方法的底层原理,是编写高质量并发程序的关键。

目录
相关文章
|
3月前
|
Java 测试技术 API
CompletableFuture 异步编程全解:核心能力、编排方案、异常处理与超时控制
本文深入解析Java异步编程核心工具CompletableFuture:涵盖底层原理(接口实现、线程模型、状态流转)、40+核心API用法(任务创建、串行转换、扁平化thenCompose、双/多任务编排),并详解生产级方案——异常处理、超时控制、电商订单页实战及五大避坑指南,助你写出高性能、高可靠异步代码。
542 1
|
机器学习/深度学习 人工智能 自然语言处理
Transformer:Attention机制、前馈神经网络、编码器与解码器
Transformer:Attention机制、前馈神经网络、编码器与解码器
1099 1
|
5月前
|
人工智能 安全 机器人
AI智能体的开发费用
AI智能体开发已分级定价:轻量级5–15万(单场景MVP),中级20–60万(多步工作流),企业级100万+(多Agent协同+合规部署)。成本聚焦架构编排、工具集成、提示工程与安全评估,另含Token消耗、算力及数据治理等隐性支出。建议先做PoC验证效果。
|
4月前
|
SQL 运维 容灾
别等业务中断才补坑!RTO/RPO 核心逻辑与全场景灾备架构选型全攻略
本文系统解析容灾备份核心知识:阐明其作为业务“生命线”的必要性;深度解读RTO(恢复时间目标)与RPO(恢复点目标)的定义、误区及量化方法;厘清备份(保数据)与容灾(保业务)的本质区别;详解冷备、温备、主从热备、同城双活、两地三中心六大主流架构;提供分场景选型指南与Spring Boot+MySQL实战代码,并强调演练验证与3-2-1运维法则。
1104 1
|
3月前
|
存储 安全 Java
ConcurrentHashMap 深度解析:从 JDK7 到 JDK8 的演进与并发安全保障
本文详解 Java 中 `ConcurrentHashMap` 的演进:JDK7 采用分段锁(Segment)提升并发性;JDK8 重构为 CAS + synchronized + 红黑树,支持并发扩容与更优查询性能。对比差异,剖析线程安全机制与使用要点。
310 12
|
3月前
|
存储 安全 Java
深入拆解 synchronized:从偏向锁到重量级锁的升级之旅与优化秘籍
本文深入剖析Java中synchronized的底层实现:详解偏向锁、轻量级锁到重量级锁的升级机制,结合对象头Mark Word结构、JVM锁优化(自旋、消除、粗化、逃逸分析),并附死锁排查实战,助你真正掌握并发同步原理。
253 3
|
4月前
|
Kubernetes Java 调度
Java 开发者的 Kubernetes 通关指南:从部署原理到运维实战,底层逻辑一次讲透
本文系统讲解Java应用在Kubernetes中的落地实践,涵盖核心架构适配、容器化要点(JVM与Cgroup协同)、Deployment/Service/Ingress等关键资源详解、调度原理与优化(反亲和性、拓扑分布等)、滚动/蓝绿/金丝雀发布策略、HPA弹性伸缩、监控告警及10大高频坑点规避,助力Java开发者真正掌握云原生运维能力。
402 3
|
3月前
|
人工智能 自然语言处理 安全
【含新版链接】小白实操指南 OpenClaw(小龙虾)Windows 一键部署
2026最新版OpenClaw(小龙虾)Windows一键部署教程:零代码、纯本地、10分钟养出你的AI数字员工!支持文件整理、浏览器自动化、微信办公等,全程可视化操作,小白友好,隐私安全有保障。(239字)
【含新版链接】小白实操指南 OpenClaw(小龙虾)Windows 一键部署
|
3月前
|
人工智能 JSON Java
Spring AI 整合火山引擎豆包向量库:我踩过的 10 个致命坑与终极解决方案
本文详述Spring AI 1.1.2整合火山引擎豆包向量模型的实战踩坑全过程,涵盖10大典型问题(如404、空指针、API Key中文、模型ID混淆等),提供根因分析与可直接复用的解决方案,并附完整多模态向量化、内存向量库及问答系统代码。
1232 1

热门文章

最新文章