Java线程

线程

Java线程状态

  • Java线程在其生命周期中会经历不同的状态,Java语言在Thread.State枚举中明确定义了6种线程状态:
public enum State {
    NEW, 			// 线程尚未启动。				此时线程对象已存在,但还没有调用start()方法。 
    RUNNABLE, 		// 线程正在运行或等待系统资源。   RUNNABLE转换条件:调用线程的start()方法。
    BLOCKED,		// 线程等待监视器锁
    WAITING,		// 线程无限期等待其他线程执行特定动作
    TIMED_WAITING,  // 线程等待指定时间
    TERMINATED;     // 线程已执行完毕
}

TIMED_WAITING进入方式:

(1)Thread.sleep(long)

(2)Object.wait(long)

(3)Thread.join(long)

(4)LockSupport.parkNanos()

(5)LockSupport.parkUntil()

BLOCKED进入方式:

等待进入synchronized方法/块

synchronized(lock) {  // 如果其他线程持有lock,当前线程会进入BLOCKED状态
    // 获取到锁后进入RUNNABLE状态
}

特点:

  • 必须有两个以上线程竞争同一把锁才会出现

  • 不消耗CPU资源

  • 不可中断

比较 BLOCKED与TIMED_WAITING:

TIMED_WAITING:由sleep()、wait(timeout)等方法引起,是主动等待

BLOCKED:由锁竞争引起,是被动阻塞

WAITING(无限期等待状态)进入方式:

调用Object.wait()

调用Thread.join()

调用LockSupport.park()

TERMINATED状态:

含义:线程执行完毕(run()方法正常结束)或因异常退出后的状态。

进入TERMINATED状态

(1)、正常完成执行

new Thread(() -> {
    System.out.println("执行任务...");
    // run()方法自然结束
}).start();

(2)、抛出未捕获的异常

new Thread(() -> {
    throw new RuntimeException("意外错误");
}).start();

注意:

一旦进入TERMINATED状态,线程就不能再次启动,具有不可逆性;

线程栈等资源会被JVM回收,但Thread对象本身仍然存在;

可通过getState()方法检测,isAlive()方法返回false

TERMINATED状态与其他状态的关系

NEW → RUNNABLE → (BLOCKED/WAITING/TIMED_WAITING) → RUNNABLE → TERMINATED

总结

状态变化简易图

      实例化
       │
       ▼
     NEW (新建) 
       │
       │ Thread.start()
       ▼
   ┌─────────────┐
   │ RUNNABLE    │◄──────────────────────┐
   │ (可运行)     │                       │
   ├─────┬───────┤                       │
   │READY│RUNNING│                       │
   └─────┴───────┘                       │
       │    │                            │
       │    │ 竞争锁失败                  │
       │    ▼                            │
       │ BLOCKED (阻塞)                   │
       │    │                            │
       │    │ 获取到锁                    │
       │    ▼                            │
       │ wait()/join()/park()          notify()/unpark()
       ▼    │                            │
   WAITING (无限等待)                     │
       │    │                            │
       │    │ sleep(timeout)/wait(timeout)│
       ▼    ▼                            │
 TIMED_WAITING (超时等待)                 │
       │    │                            │
       │    │ 超时/被唤醒                  │
       └────┴────────────────────────────┘
               │
               │ 执行完成
               ▼
           TERMINATED (终止)

Java线程状态2

java线程的五大状态,阻塞状态详解_线程阻塞状态-CSDN博客

image-20250721103240529

  • 每一个对象都有一个无形的锁,sleep不会释放锁。(也就是我们说过的,抱着资源睡觉,这个特点对比wait)

JVisualVm显示线程状态1

image

jVisualVM 在 Java 标准状态的基础上进一步细分(尤其是阻塞状态):

jVisualVM 状态 jVisualVM显示 对应 Java 状态 详细说明
RUNNABLE 运行 RUNNABLE 线程正在执行或可运行。
WAITING 等待(先持有锁) WAITING 通用等待状态,可能由 Object.wait() 或 LockSupport.park() 触发。
PARKED 驻留(不需要锁) WAITING 线程调用 LockSupport.park() 进入的等待状态(显示为 WAITING (parking))。
BLOCKED 监视 BLOCKED 线程等待进入 synchronized 块(显示为 BLOCKED (on object monitor))。
MONITOR 监视(锁机制的统称) BLOCKED/WAITING 与监视器锁相关的状态(如竞争锁或调用 wait() 后释放锁)。
TIMED_WAITING 休眠(支持外部唤醒和锁释放) TIMED_WAITING 限时等待(如 sleep()、LockSupport.parkNanos())。
SLEEPING 休眠(仅超时唤醒,不释放锁) TIMED_WAITING 线程调用 Thread.sleep() 进入的状态(jVisualVM 可能单独标记)。
结束 结束:空白的为结束 TERMINATED 线程执行完毕(run()方法正常结束)或因异常退出后的状态。

一句话记忆:

sleep() 是“闹钟”,到点才醒;
TIMED_WAITING 是“待机模式”,可被提前叫醒。

关键区别与说明

  1. PARKED 是 WAITING 的子集
    • Java 标准状态中,LockSupport.park() 触发的阻塞归类为 WAITING。
    • 但 jVisualVM 会明确标记为 PARKED(显示为 WAITING (parking)),便于区分传统的 Object.wait()。
  2. MONITOR 状态
    • 在 Java 中,synchronized 锁竞争导致 BLOCKED,而 wait() 导致 WAITING。
    • jVisualVM 可能统一标记为 MONITOR,或在堆栈中注明 on object monitor。
  3. SLEEPING 是 TIMED_WAITING 的特例
    • Thread.sleep() 在 Java 中属于 TIMED_WAITING,但 jVisualVM 可能单独显示为 SLEEPING。

关键对比

状态 触发方法 CPU 占用 唤醒条件 典型场景
休眠 Thread.sleep() 0% 超时结束 定时任务、模拟延迟
驻留 LockSupport.park() 0% 调用 unpark() 线程池空闲等待、AQS 锁实现
监视-BLOCKED synchronized 竞争失败 0% 锁释放 高并发锁竞争
监视-WAITING Object.wait() 0% notify()/notifyAll() 生产者-消费者模型

线程的状态对比:等待、驻留、监视

线程的状态对比:等待、驻留、监视 - deyang - 博客园

JVisualVm显示线程状态2

JVisualVM 中线程状态(运行/休眠/等待/驻留/监视)解析

jVisualVM显示线程状态:

image

  • 运行(runnable):正在运行中的线程。
  • 休眠(timed_waiting):休眠线程,例如调用Thread.sleep方法。
  • 等待(waiting):等待唤醒的线程,可通过调用Object.wait方法获得这种状态,底层实现是基于对象头中的monitor对象。
  • 驻留(waiting):等待唤醒的线程,和等待状态类似,只不过底层的实现方式不同,处于这种状态的例子有线程池中的空闲线程,等待获取reentrantLock锁的线程,调用了reentrantLock的condition的await方法的线程等等,底层实现是基于Unsafe类的park方法,在AQS中有大量的应用。
  • 监视(blocked):等待获取monitor锁的线程,例如等待进入synchronize代码块的线程。

JVisualVm显示线程

JVisualVm显示线程的线程不完整,想看所有的线程使用 jstack 。或者在JVisualVm里点击线程Dump

image-20250721131751848

主要系统线程

  1. main - 主线程,执行程序的 main() 方法
  2. Reference Handler - 处理引用对象的线程(如软引用、弱引用等)
  3. Finalizer - 调用对象的 finalize() 方法的线程
  4. Signal Dispatcher - 处理操作系统发送给 JVM 信号的线程
  5. Attach Listener - 用于接收外部工具(如 jvisualvm)连接的线程

垃圾回收相关线程

JVisualVm线程界面未显示GC线程,点线程dump可以看到

  1. GC 线程(名称可能不同):
    • "GC Thread#0"、"GC Thread#1" 等
    • 执行垃圾收集操作的线程
    • 数量和名称取决于使用的垃圾收集器
  2. VM Thread - JVM 系统线程,执行虚拟机操作

其他常见线程

  1. CompilerThread0/1 - JIT 编译线程,将字节码编译为本地代码

  2. JDWP Command Reader - Java Debug Wire Protocol (JDWP) 的工作线程

    • 调试通信:在 Java 调试过程中,负责读取来自调试器(如 IntelliJ IDEA、Eclipse、jdb)的调试命令。

    • 协议处理:解析 JDWP 协议数据包,并将调试指令传递给 JVM 的调试子系统。

    • 触发条件:该线程仅在以下场景出现:

      • 主动启用调试模式:通过 JVM 参数 -agentlib:jdwp=... 启动调试功能。

        java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar
        
      • IDE 调试时:当使用 IntelliJ/Eclipse 的调试功能时,IDE 会自动附加到目标进程并激活 JDWP。

  3. **Service Thread ** - JVM 内部服务:负责执行 JVM 的底层后台任务,例如:

    • 垃圾回收(GC)相关的辅助操作(如并发标记阶段的处理)。
    • 动态代码反优化(Deoptimization)的清理工作。
    • 其他 JVM 内部维护任务(如方法编译队列的处理)。
  4. VM Periodic Task Thread - 该线程负责执行 JVM 内部的周期性任务,包括但不限于:

    • 性能监控数据采集(如 JVM 统计信息、性能计数器更新)
    • 动态代码优化/去优化(如 JIT 编译后的代码调整)
    • 内存管理辅助任务(如某些 GC 算法的后台维护)
    • 其他 JVM 内部定期维护任务(如检查死锁、调整线程优先级)
  5. Monitor Ctrl-Break - 是一个由 IntelliJ IDEA(或其他一些 Java IDE)在运行或调试 Java 程序时自动创建的辅助线程,主要用于 监控用户输入(如 Ctrl+C 或 Ctrl+Break),以便在控制台程序被强制终止时执行清理操作(如关闭资源、生成线程转储等)。

  6. 常见的 JMX 相关线程。在 jstack 或 jvisualvm 中,你可能会看到以下 JMX 相关的线程:

    线程名称 作用
    RMI TCP Connection 处理 JMX 客户端(如 JConsole、VisualVM)的远程连接请求。
    RMI Scheduler 调度 RMI(Remote Method Invocation)相关任务。
    JMX server connection 负责 JMX 服务端的连接管理(如 com.sun.jmx.remote.internal.ServerCommunicatorAdmin)。
    RMI RenewClean 清理过期的 RMI 连接(租约续订机制)。
    RMI GC Daemon RMI 分布式垃圾回收(DGC)相关的线程。
    RMI TCP Accept 监听 JMX/RMI 的 TCP 连接请求(通常在端口 1099 或自定义端口)。
    • 这样线程出现的情况:

      • 当启用 JMX 远程监控时,例如:

        java -Dcom.sun.management.jmxremote.port=9010 \
             -Dcom.sun.management.jmxremote.authenticate=false \
             -Dcom.sun.management.jmxremote.ssl=false \
             -jar myapp.jar
        

        此时 JVM 会启动 RMI 和 JMX 相关线程 以支持远程管理。

      • 即使未进行启动配置jmx,JVisualVm本地连接java进程时,也会启动这些线程。

  7. WatcherThread - 监控 JVM 状态的线程

  8. Common-Cleaner - 用于清理资源的线程(Java 9+)

守护线程

如何区分守护线程 vs 用户线程?

特征 守护线程(如 VM Thread) 用户线程(如 main)
阻止 JVM 退出? ❌ 不会 ✅ 会
创建者 JVM 内部创建 用户代码或默认线程(如 main)
典型例子 GC 线程、JMX 线程、VM Thread main、自定义未设 setDaemon 的线程
jstack 标记 可能显示 daemon(部分 JVM 版本省略) 无 daemon 标记

上面列举的那么多线程:主要系统线程、垃圾回收相关线程、其他常见线程。只有main线程是用户线程,其他都是守护线程

jstack打印细节解析

"main" #1 prio=5 os_prio=0 tid=0x0000000003693800 nid=0xc50 waiting on condition [0x000000000311f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.sh.demo.Test.main(Test.java:29)

   Locked ownable synchronizers:
        - None

1. 线程名称

"main"
  • 表示这是 Java 主线程,即执行 public static void main(String[] args) 的线程。

2. 线程编号

#1
  • 该线程在 JVM 中的 内部编号(第一个创建的线程)。

3. 线程优先级

prio=5
  • Java 线程优先级(范围:1~10,默认是 5)。
  • Java 线程优先级是数值越大优先级越高(1~10 ,10最高),但实际效果依赖操作系统调度,通常不建议依赖优先级控制线程行为。
  • 注意:Java 的优先级不一定完全映射到操作系统优先级。

4. 操作系统优先级

os_prio=0
  • 操作系统级别的线程优先级(具体含义依赖 OS)。
  • 在 Linux 中,0 表示普通优先级。

5. 线程 ID(Java 内部标识)

tid=0x0000000003693800
  • JVM 内部的线程对象地址(十六进制),用于唯一标识该线程。

6. 原生线程 ID(操作系统层)

nid=0xc50
  • Native Thread ID(十六进制),对应操作系统的线程 ID。
  • 转换十进制:0xc50 = 3152(可通过 top -H -p <PID> 查看该线程的 CPU 占用)。

7. 线程状态

waiting on condition [0x000000000311f000]
  • 等待条件:线程因某种原因(如 sleep、wait、LockSupport.park)进入等待状态。
  • [0x000000000311f000]:线程栈的起始内存地址(通常无需关注)。

8. Java 线程状态

java.lang.Thread.State: TIMED_WAITING (sleeping)
  • TIMED_WAITING:线程正在执行 限时等待。
  • (sleeping):具体原因是通过 Thread.sleep() 进入等待状态。

9. 调用堆栈

at java.lang.Thread.sleep(Native Method)
at com.sh.demo.Test.main(Test.java:29)
  • 堆栈轨迹:
    1. 当前正在执行 Thread.sleep()(Native 方法,由 JVM 实现)。
    2. 调用来源:Test.main() 方法的第 29 行(代码中显式调用了 sleep)。

10. 持有的锁

Locked ownable synchronizers: - None
  • 未持有任何显式锁(如 ReentrantLock)。
  • 如果是 synchronized 锁,不会在此显示(需看其他线程的 BLOCKED 状态)。

posted @ 2025-08-06 14:05  deyang  阅读(71)  评论(0)    收藏  举报