JAVA并发学习

一、虚拟线程与载体线程

  1. 核心概念
    载体线程 = 平台线程 = 操作系统线程(OS 线程),是 JVM 直接使用的 “真实线程”,不是 CPU 硬件线程。
    虚拟线程:挂载在载体线程上执行,由 JVM 在用户态调度,创建 / 销毁成本极低,适合高并发 IO 场景。
  2. 调度机制(挂载 / 卸载)
    挂载:虚拟线程需要执行时,调度器将其绑定到空闲载体线程。
    卸载:虚拟线程遇到阻塞(如 IO、wait)时,JVM 保存其栈状态,将其从载体线程上卸下,释放载体线程去执行其他虚拟线程。
    重挂载:阻塞结束后,调度器重新找空闲载体线程挂载虚拟线程(不一定是原载体)。
  3. 虚拟线程的唤醒问题
    ❌ 不能指名道姓唤醒某个具体线程(平台线程 / 虚拟线程都不行)。
    ✅ 可以通过锁分组 / Condition 等待队列实现精准唤醒某一类线程(如只唤醒消费者,不唤醒生产者)。
    二、线程通信与等待 / 通知机制
  4. wait ()/notify () 的使用限制
    必须在synchronized同步块中:因为它们操作的是锁对象的等待队列,必须先持有锁才能修改队列,否则会抛出IllegalMonitorStateException。
    本质:防止并发修改等待队列导致状态错乱,保证线程安全。
  5. 锁对象内部结构
    每个 Java 对象(锁)内部包含:
    等待队列(Wait Set):存放调用wait()的线程。
    阻塞队列(Entry Set):存放抢锁失败、等待锁的线程。
    持有线程指针:记录当前持有锁的线程。
    锁计数器(Hold Count):支持synchronized可重入,记录重入次数,确保退出次数 = 进入次数时才释放锁,避免自己锁死自己。
  6. Condition 与精准唤醒
    lock.newCondition():创建一个独立的等待队列(等待室),一个 Lock 可以创建多个 Condition。
    condition.await():线程进入该 Condition 的等待队列等待。
    condition.signal():唤醒该等待队列中的一个线程。
    condition.signalAll():唤醒该等待队列中的所有线程。
    优势:将生产者 / 消费者分开到不同 Condition 等待,实现精准通知(如只唤醒消费者,不唤醒生产者)。
  7. 生产者 - 消费者模型的通信逻辑
    队列满时:生产者在notFull Condition 等待。
    队列空时:消费者在notEmpty Condition 等待。
    生产者生产后:调用notEmpty.signal(),只唤醒消费者。
    消费者消费后:调用notFull.signal(),只唤醒生产者。
    本质:精准唤醒某一类线程,而非某个具体线程。
    三、线程控制工具
  8. join () 方法
    作用:t.join() → 当前线程等待 t 线程执行完毕后再继续执行。
    底层:通过while (t.isAlive()) { wait(); }实现,t 线程结束后 JVM 自动唤醒等待线程。
    带超时:t.join(ms) → 最多等待指定毫秒数,超时后继续执行。
  9. jstack 命令(线程排查工具)
    作用:打印 Java 进程的线程堆栈,用于排查死锁、阻塞、线程状态。
    常用命令:
    jps -l:查找 Java 进程 PID。
    jstack :查看所有线程堆栈。
    jstack -l :查看锁信息,自动检测死锁。
    jstack -v :查看虚拟线程(JDK 21 + 支持)。
    关键线程状态:
    RUNNABLE:正在运行。
    WAITING:调用wait()/await()/join()无超时等待。
    BLOCKED:等待synchronized锁。
    TIMED_WAITING:调用sleep()/wait(ms)/join(ms)限时等待。
    四、协程与传统线程的核心区别
    维度 传统线程 协程 / 虚拟线程
    调度者 操作系统内核 应用程序 / 语言 Runtime(如 JVM)
    调度模式 抢占式(内核可随时强制切换) 非抢占式(程序员通过yield/await显式控制切换)
    切换成本 高(内核态上下文切换) 低(用户态上下文切换)
    阻塞处理 阻塞时占用内核线程资源 阻塞时释放载体线程,资源可复用
    适用场景 CPU 密集型 IO 密集型
    创建成本 高(需内核分配资源) 极低(仅 JVM 堆对象 + 栈)
    五、关键易错点总结
    精确唤醒误区:Java 中无法直接唤醒某个具体线程,只能通过锁 / 条件分组唤醒某一类线程。
    wait ()/notify () 必须在同步块中:否则会抛出异常,且无法保证线程安全。
    虚拟线程与平台线程行为一致:虚拟线程同样支持wait()/notify()/notifyAll(),阻塞时会释放载体线程。
    锁计数器的作用:支持synchronized可重入,防止同一线程多次加锁时自己锁死自己。
    Condition 本质:是独立的等待队列,用于替代synchronized单一等待队列,实现精准唤醒。
posted @ 2026-03-27 11:00  sxxmk  阅读(7)  评论(0)    收藏  举报