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. PARKEDWAITING 的子集
    • Java 标准状态中,LockSupport.park() 触发的阻塞归类为 WAITING
    • 但 jVisualVM 会明确标记为 PARKED(显示为 WAITING (parking)),便于区分传统的 Object.wait()
  2. MONITOR 状态
    • 在 Java 中,synchronized 锁竞争导致 BLOCKED,而 wait() 导致 WAITING
    • jVisualVM 可能统一标记为 MONITOR,或在堆栈中注明 on object monitor
  3. SLEEPINGTIMED_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 相关线程。在 jstackjvisualvm 中,你可能会看到以下 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]
  • 等待条件:线程因某种原因(如 sleepwaitLockSupport.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  阅读(17)  评论(0)    收藏  举报