线程的状态对比:等待、驻留、监视(阻塞、等待)

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

等待(waiting)和监视(blocked)的区别

等待(waiting)和监视(blocked)的区别 ?

官方线程状态定义

public enum State {
 NEW,
 RUNNABLE,
 BLOCKED,    // 阻塞
 WAITING,    // 等待
 TIMED_WAITING, // 超时等待
 TERMINATED
}

核心区别对比

特性 BLOCKED(阻塞) WAITING(等待)
触发条件 等待进入synchronized同步块 调用Object.wait()、Thread.join()、LockSupport.park()
唤醒方式 获取到监视器锁时自动唤醒 需要其他线程主动唤醒(notify/notifyAll)
锁的状态 在竞争锁,但还没拿到 已经释放了锁,在等待条件
使用场景 简单的互斥访问 复杂的线程协作
超时机制 无超时,一直等待锁 可以有超时(TIMED_WAITING)或无超时

总结
关键区别记忆点:

  • BLOCKED:"我想进去,但门锁着,我在门口等着"
  • WAITING:"我主动休息了,你好了叫我"

游戏服务器中的指导原则:

  1. BLOCKED过多 → 锁竞争激烈,需要优化锁策略
  2. WAITING正常 → 线程协作,是设计的一部分
  3. 监控两者比例 → BLOCKED应该远少于WAITING
  4. 优先使用java.util.concurrent → 比synchronized + wait/notify更高效

等待(waiting)和监视(blocked)都算是阻塞吗?

都算是阻塞吗?

从Java线程状态机来看:BLOCKED和WAITING是两种不同的状态,但都可以被认为是广义的"阻塞"。

// Java线程状态机中的关系
RUNNABLE → BLOCKED     // 等待锁 → 广义阻塞
RUNNABLE → WAITING     // 主动等待 → 广义阻塞  
RUNNABLE → TIMED_WAITING // 超时等待 → 广义阻塞

详细分析

  1. 从CPU执行角度:都是阻塞
public class CpuPerspective {
    public static void main(String[] args) {
        // 无论是BLOCKED还是WAITING状态:
        // - 线程都不在CPU上执行指令 ✓
        // - 都不消耗CPU计算资源 ✓
        // - 都在等待某种条件满足 ✓
        
        // 因此从CPU调度器角度看,它们都是"不运行"的状态
    }
}
  1. 从阻塞原因角度:有重要区别
阻塞类型 原因 恢复条件
同步阻塞 (BLOCKED) 竞争同步锁 锁可用时自动恢复
等待阻塞 (WAITING) 主动放弃CPU 需要其他线程显式唤醒
I/O阻塞 等待I/O操作 I/O完成时恢复

线程池没有任务来时,所有核心线程会是等待(waiting)状态?

线程池中没有任务时,核心线程会处于WAITING状态,但具体机制有所不同。

不同类型的等待

  1. 使用LinkedBlockingQueue的线程池
public class LinkedBlockingQueueExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 30, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>()  // 无界队列
        );
        
        executor.prestartAllCoreThreads();
        
        // 空闲线程状态: WAITING
        // 阻塞在: java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject
        // 具体是: LinkedBlockingQueue.take() 方法中的condition.await()
    }
}
  1. 使用SynchronousQueue的线程池
public class SynchronousQueueExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        // 内部使用SynchronousQueue
        
        // 空闲线程状态: WAITING  
        // 阻塞在: SynchronousQueue的transfer方法
        // 等待其他线程提交任务
    }
}
  1. 使用ArrayBlockingQueue的线程池
public class ArrayBlockingQueueExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 30, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10)  // 有界队列
        );
        
        // 空闲线程状态: WAITING
        // 阻塞在: ArrayBlockingQueue的condition.await()
    }
}

不同的队列不同的实现:

/**
* LinkedBlockingQueue: 
* - 空闲线程: WAITING on ConditionObject
* - 队列空时阻塞在take()
* 
* SynchronousQueue:
* - 空闲线程: WAITING on transfer stack
* - 没有缓冲区,直接传递
* 
* DelayQueue:
* - 空闲线程: TIMED_WAITING
* - 等待特定延迟时间
*/

/**
* 空闲的核心线程资源占用:
* - 内存: 每个线程约1MB栈内存 (通过-Xss设置)
* - 内核资源: 每个线程占用操作系统线程结构
* - CPU: 几乎为0 (WAITING状态不消耗CPU)
* 
* 示例: 10个核心线程空闲
* - 内存: 10MB
* - CPU: 0%
* - 状态: 全部WAITING
*/

总结

是的,线程池没有任务时,核心线程处于WAITING状态,这是正常且期望的行为:

正常现象:

  • 所有空闲核心线程都处于WAITING状态
  • 阻塞在工作队列的获取方法上(如take())
  • 不消耗CPU资源,只占用少量内存

简单来说:WAITING的空闲线程就像待命的士兵,随时准备执行任务,这是线程池高效工作的基础机制!

等待(Wait)和驻留(Park)的区别

方面 Wait Park
所属包 java.lang.Object java.util.concurrent.locks.LockSupport
需要锁吗 ✅ 必须在synchronized块内 ❌ 不需要持有锁
会释放锁吗 ✅ 会释放锁 ❌ 不涉及锁操作
唤醒方式 notify()/notifyAll() unpark(thread)
超时支持 wait(timeout) parkNanos(timeout)
中断响应 ✅ 抛出InterruptedException ✅ 直接返回,不抛异常

无论是等待还是驻留,线程在WAITING状态下的资源占用模式是相同的:

  • CPU占用:几乎为0
  • 内存占用:固定(线程栈大小)
  • 系统资源:少量内核结构

代码示例对比

  1. Wait 的使用
public class WaitExample {
    private static final Object lock = new Object();
    
    public void doWait() throws InterruptedException {
        synchronized (lock) {  // 必须先获得锁
            System.out.println("准备wait,会释放锁");
            lock.wait();       // 释放锁,进入WAITING状态
            System.out.println("被notify唤醒了,重新获得锁");
        }
    }
    
    public void doNotify() {
        synchronized (lock) {  // 必须获得同一把锁
            lock.notify();     // 唤醒一个等待线程
        }
    }
}
  1. Park 的使用
public class ParkExample {
    public void doPark() {
        System.out.println("准备park,不需要锁");
        LockSupport.park();    // 直接挂起,不需要锁
        System.out.println("被unpark唤醒了");
    }
    
    public void doUnpark(Thread thread) {
        LockSupport.unpark(thread);  // 随时可以唤醒指定线程
    }
}

总结

简单记忆:

  • Wait:"老式"方式,必须配合synchronized使用,会释放锁
  • Park:"现代"方式,更灵活,不需要锁,性能更好

在并发编程中的选择:

  • 需要与synchronized配合时用 Wait
  • java.util.concurrent包中的高级并发工具中用 Park
  • 需要精确控制特定线程时用 Park

VisualVM中看到:

  • "Wait" → 传统的wait()/notify()机制
  • "Park" → 现代的LockSupport.park()/unpark()机制

两者都是等待,但底层机制和适用场景完全不同!🎯

Park状态的出现时机

  • Java 5 引入JUC包时出现
  • 主要在各种并发工具内部使用
  • 线程池、队列、锁等底层实现

现状:

  • Park/Unpark: 现代并发首选,框架广泛使用
  • ⚠️ Wait/Notify: 遗留代码、简单场景、教学示例
  • 🎯 推荐: 直接使用JUC高级工具,而不是手动park/wait

简单历史总结:

1996-2004: wait/notify 为主流
2004-现在: park/unpark + JUC工具 成为主流
现在: 95%+的新代码使用JUC,而不是直接wait/notify

现代Java开发中,我们更应该关注如何使用好JUC工具,而不是纠结于底层的park/wait选择!🎯

Park状态和Park线程的区别分析

方面 Park状态 Park线程
定义 线程的瞬时状态 线程的身份性质
时间范围 短暂的,可变化的 相对持久的
关注点 线程当前在干什么 线程的用途和角色

简单判断:

  • 如果线程偶尔park一下 → 只是处于Park状态
  • 如果线程主要工作就是park等待 → 是Park线程

Park线程的判断标准:

  1. 主要工作时间处于Park状态 (>80%时间)
  2. 设计用途就是等待事件/任务
  3. 执行的工作是次要的,等待是主要的

一段代码展示等待、驻留、监视

状态名称 技术术语 触发方式 核心特点
监视 BLOCKED 锁竞争失败 被动等待,不释放资源
等待 WAITING wait()调用 主动释放锁,等待条件
驻留 PARK park()调用 轻量级,不涉及锁操作
// 核心代码如下

// 🔒 BLOCKED - 锁竞争
synchronized(lock) { ... }

// ⏳ WAITING - 条件等待  
lock.wait();

// 🅿️ PARK - 轻量驻留
LockSupport.park();
LockSupport.unpark(thread);
// 三种线程状态演示
public class ThreadStateDemo {
    private static Object lock = new Object();
    private static Thread parkThread;

    public static void main(String[] args) {
        // 🔒 阻塞状态 - 线程竞争锁失败
        new Thread(() -> {
            synchronized(lock) {
                sleep(2000); // 持有锁
            }
        }).start();
        
        new Thread(() -> {
            synchronized(lock) { // 🟡 这里会BLOCKED
                // 获得锁后执行
            }
        }).start();

        // ⏳ 等待状态 - 主动释放锁等待
        new Thread(() -> {
            synchronized(lock) {
                lock.wait(); // 🟡 释放锁,进入WAITING
                // 被notify后继续
            }
        }).start();

        // 🅿️ 驻留状态 - 轻量级等待
        parkThread = new Thread(() -> {
            LockSupport.park(); // 🟡 进入PARK状态
            // 被unpark后继续
        });
        parkThread.start();
        
        LockSupport.unpark(parkThread); // 唤醒
    }
    
    static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (Exception e) {}
    }
}

BLOCKED、WAITING、TIMED_WAITING状态进入方法

BLOCKED:

  • 进入 synchronized 竞争锁

     synchronized(lock) {  // 锁被占用 → BLOCKED
         // ...
     }
    
  • wait 后重新竞争锁

    synchronized (obj) {
        obj.wait(); // 释放锁 → WAITING
        // 被notify()唤醒后,重新竞争锁失败后还是BLOCKED状态
        // 被唤醒后重新竞争锁 → 可能BLOCKED
    } 
    

WAITING:

  • Object#wait() Object.wait with no timeout
  • Thread#join() Thread.join with no timeout
  • LockSupport#park() LockSupport.park

TIMED_WAITING:

  • Thread#sleep Thread.sleep
  • Object#wait(long) Object.wait with timeout
  • Thread##join(long) Thread.join with timeout
  • LockSupport#parkNanos LockSupport.parkNanos
  • LockSupport#parkUntil LockSupport.parkUntil

源码:

/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,

/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
*   <li>{@link Object#wait() Object.wait} with no timeout</li>
*   <li>{@link #join() Thread.join} with no timeout</li>
*   <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,

/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
*   <li>{@link #sleep Thread.sleep}</li>
*   <li>{@link Object#wait(long) Object.wait} with timeout</li>
*   <li>{@link #join(long) Thread.join} with timeout</li>
*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,

广义阻塞的定义

public enum State {
 NEW,
 RUNNABLE,
 BLOCKED,    // 阻塞
 WAITING,    // 等待
 TIMED_WAITING, // 超时等待
 TERMINATED
}

狭义: "我在门口等着进房间" → BLOCKED
广义: "我在任何地方等待" → 所有不消耗CPU的状态

广义阻塞 = 线程不消耗CPU时间的状态:包括:BLOCKED, WAITING, TIMED_WAITING。

狭义阻塞:BLOCKED。

  • BLOCKED: 等待锁,CPU≈0%

  • WAITING: 等待条件,CPU≈0%

  • TIMED_WAITING: 超时等待,CPU≈0%

  • RUNNABLE: 可变 (真正消耗CPU)

  • NEW/TERMINATED: CPU=0% (未开始/已结束)

posted @ 2025-10-15 14:00  deyang  阅读(8)  评论(0)    收藏  举报