ReadStackLog源码阅读


import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.SneakyThrows;

public class ReadStackLog {
    public static void main(String[] args) throws JsonProcessingException {
        new Thread(new TimeWaiting (), "TimeWaitingThread").start();
        new Thread(new Waiting(), "WaitingThread").start();
        // 使用两个Blocked线程,一个获取锁成功,另一个被阻塞
        new Thread(new Blocked(), "BlockedThread-1").start();
        new Thread(new Blocked(), "BlockedThread-2").start();
    }
}
// 该线程不断地进行睡眠
class TimeWaiting implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
// 该线程在Waiting.class实例上等待; 该线程进入等待状态(WAITING),等待一个对象的通知。
class Waiting implements Runnable {
    @Override
    public void run() {
        while (true) {
            synchronized (Waiting.class) {
                try {
                    Waiting.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
// 该线程在Blocked.class实例上加锁后,不会释放该锁
class Blocked implements Runnable {
    @SneakyThrows
    public void run() {
        synchronized (Blocked.class) {
            while (true) {
                try {
                    Thread.sleep(1000000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}



程序功能概述

这个程序主要启动了四个线程,每个线程模拟不同的线程状态:

  1. TimeWaitingThread: 该线程不断地进入睡眠状态(TIMED_WAITING)。
  2. WaitingThread: 该线程进入等待状态(WAITING),等待一个对象的通知。
  3. BlockedThread-1BlockedThread-2: 这两个线程尝试获取同一个锁(Blocked.class)。由于 BlockedThread-1 先获得锁,BlockedThread-2 将一直被阻塞(BLOCKED)。

代码详解

  • 主方法 (main):启动四个线程,每个线程对应一个不同的任务。
  • TimeWaiting 类
    • 该线程在 run 方法中进入一个无限循环,每次循环调用 Thread.sleep(1000000),即让线程进入睡眠状态。
    • 这种状态在 Java 中被称为 TIMED_WAITING,表示线程在等待一段时间后会自动恢复运行。
  • Waiting 类
    • 该线程在 run 方法中进入一个无限循环,首先获得 Waiting.class 的锁(同步块),然后调用 Waiting.class.wait() 使线程进入等待状态。
    • wait() 方法会使线程释放锁,并进入等待状态,直到有其他线程调用同一个锁的 notify()notifyAll() 方法(在此程序中没有其他线程,因此该线程将一直等待)。
    • 这种状态在 Java 中被称为 WAITING
  • Blocked 类
    • 该线程在 run 方法中首先尝试获取 Blocked.class 的锁(同步块)。
    • 第一个启动的 BlockedThread-1 获得锁并进入无限睡眠状态。
    • 第二个启动的 BlockedThread-2 尝试获取相同的锁,但由于锁已被 BlockedThread-1 持有,它将被阻塞,直到锁被释放。
    • 这种被阻塞等待锁的状态在 Java 中称为 BLOCKED

2. 命令行工具 jpsjstack 的作用

jps 工具

jps(Java Virtual Machine Process Status Tool)是 JDK 提供的一个工具,用于列出当前系统上正在运行的 Java 应用进程。它的输出包括每个 Java 进程的进程ID(PID)和主类名。

你运行的命令和输出示例如下:

D:\B_IDEA\testE_09\verifyRedisCache>jps
49060 
40860 Jps
49580 ReadStackLog
51148 Launcher
  • 49060:可能是一个没有主类名的 Java 进程。
  • 40860 Jpsjps 工具本身的进程ID。
  • 49580 ReadStackLog:你刚才编写并运行的 ReadStackLog 程序的进程ID。
  • 51148 Launcher:可能是其他 Java 应用程序的进程。

jstack 工具

jstack(Java Stack Trace)用于获取指定 Java 进程的线程堆栈信息。它可以帮助开发者分析线程的状态、死锁等问题。

你运行的命令和部分输出如下:

D:\B_IDEA\testE_09\verifyRedisCache>jstack 49580
...

这里,49580ReadStackLog 程序的进程ID。


3. jstack 输出的线程信息详解

以下是 jstack 输出的部分内容及其解释:

主要线程分析

  1. TimeWaitingThread (#20)
 "TimeWaitingThread" #20 prio=5 os_prio=0 tid=0x000002535c3fb000 nid=0xaaf0 waiting on condition [0x000000cd0c3ff000]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
         at java.lang.Thread.sleep(Native Method)
         at TimeWaiting.run(ReadStackLog.java:20)
         at java.lang.Thread.run(Thread.java:750)

状态TIMED_WAITING,表示线程在 sleep 方法中进入了睡眠状态。
原因:调用 Thread.sleep(1000000) 进入睡眠。
2. WaitingThread (#21)

 "WaitingThread" #21 prio=5 os_prio=0 tid=0x000002535c40e800 nid=0xb420 in Object.wait() [0x000000cd0c4ff000]
    java.lang.Thread.State: WAITING (on object monitor)
         at java.lang.Object.wait(Native Method)
         at Waiting.run(ReadStackLog.java:34)
         at java.lang.Thread.run(Thread.java:750)

状态WAITING,表示线程在 wait() 方法中等待通知。
原因:调用 Waiting.class.wait() 进入等待状态,没有其他线程调用 notifynotifyAll 方法。
3. BlockedThread-1 (#22)

 "BlockedThread-1" #22 prio=5 os_prio=0 tid=0x000002535c417800 nid=0x9c98 waiting on condition [0x000000cd0c5ff000]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
         at java.lang.Thread.sleep(Native Method)
         at Blocked.run(ReadStackLog.java:49)
         at java.lang.Thread.run(Thread.java:750)

状态TIMED_WAITING,表示线程在 sleep 方法中睡眠。
原因BlockedThread-1 获得了 Blocked.class 的锁后,进入睡眠状态。
4. BlockedThread-2 (#23)

 "BlockedThread-2" #23 prio=5 os_prio=0 tid=0x000002535c418000 nid=0xb1dc waiting for monitor entry [0x000000cd0c6ff000]
    java.lang.Thread.State: BLOCKED (on object monitor)
         at Blocked.run(ReadStackLog.java:49)
         at java.lang.Thread.run(Thread.java:750)

状态BLOCKED,表示线程在等待获取锁。
原因BlockedThread-2 尝试获取 Blocked.class 的锁,但 Blocked.class 已被 BlockedThread-1 持有,因此进入阻塞状态。

其他线程分析

除了用户自定义的四个线程外,还有一些 JVM 内部线程,如:

  • C1 CompilerThreadXC2 CompilerThreadX:JIT 编译器线程,用于即时编译 Java 字节码。
  • Finalizer:负责对象终结(垃圾回收前的清理)。
  • Reference Handler:处理 Reference 对象(如 WeakReference)。
  • VM Thread:虚拟机线程,负责 JVM 的内部操作。
  • Service Thread:服务线程,负责 JVM 的一些后台服务。
  • Monitor Ctrl-BreakAttach Listener 等:JVM 的控制和监听线程。

这些线程一般都处于 RUNNABLE 状态,表示它们正在运行或等待运行。

线程状态汇总

  • RUNNABLE:线程正在运行或准备运行。
  • WAITING:线程在等待某个条件(如 waitjoinLockSupport.park)。
  • TIMED_WAITING:线程在等待某个条件或时间(如 sleepwait 带超时)。
  • BLOCKED:线程尝试获取一个锁,但被其他线程持有,无法继续执行。

4. 总体总结

你编写的 Java 程序通过启动多个线程,分别模拟了不同的线程状态:

  1. TimeWaitingThread:进入睡眠状态,展示了 TIMED_WAITING
  2. WaitingThread:进入等待状态,展示了 WAITING
  3. BlockedThread-1BlockedThread-2
    • BlockedThread-1 获得锁后进入睡眠状态,展示了 TIMED_WAITING
    • BlockedThread-2 因为锁被占用而进入阻塞状态,展示了 BLOCKED

通过使用 jpsjstack 工具,你可以观察到这些线程的具体状态及其调用堆栈。这对于理解 Java 线程状态和调试多线程应用非常有帮助。

如果你希望进一步探索,可以尝试以下操作:

  • 释放锁:在 BlockedThread-1 中添加代码在某个条件下释放锁(如不再进入睡眠),观察 BlockedThread-2 状态如何变化。
  • 通知等待线程:在 ReadStackLog 的主线程或其他线程中调用 Waiting.class.notify(),观察 WaitingThread 是否从 WAITING 状态转为 RUNNABLE
  • 增加更多线程:模拟更多复杂的线程交互,深入理解多线程编程中的各种状态和问题。

Waiting & Time Waiting

在 Java 中,WAITINGTIMED_WAITING 是两种不同的线程状态,它们的区别主要体现在线程等待的条件和等待的时间上。以下是对这两种状态的详细解释:


1. WAITING

  • 含义:线程处于无限期等待状态,直到其他线程显式唤醒它。
  • 触发条件
    • 调用 Object.wait() 方法(不带超时参数)。
    • 调用 LockSupport.park() 方法。
  • 特点
    • 线程会一直等待,直到其他线程调用 Object.notify()Object.notifyAll() 来唤醒它。
    • 如果没有其他线程唤醒它,线程会一直处于 WAITING 状态。
  • 示例
"WaitingThread" #21 prio=5 os_prio=0 tid=0x000002535c40e800 nid=0xb420 in Object.wait() [0x000000cd0c4ff000]
   java.lang.Thread.State: WAITING (on object monitor)
      at java.lang.Object.wait(Native Method)
      at Waiting.run(ReadStackLog.java:34)
      at java.lang.Thread.run(Thread.java:750)
- `WaitingThread` 调用了 `Object.wait()`,正在等待 `Waiting.class` 的锁。
- 该线程需要其他线程调用 `Object.notify()` 或 `Object.notifyAll()` 来唤醒它。

2. TIMED_WAITING

  • 含义:线程处于有限期等待状态,等待一段时间后会自动唤醒。
  • 触发条件
    • 调用 Thread.sleep(long millis) 方法。
    • 调用 Object.wait(long timeout) 方法(带超时参数)。
    • 调用 LockSupport.parkNanos(long nanos)LockSupport.parkUntil(long deadline) 方法。
  • 特点
    • 线程会等待指定的时间,时间到达后会自动唤醒。
    • 如果在等待期间被其他线程唤醒,线程也会提前结束等待。
  • 示例
"TimeWaitingThread" #20 prio=5 os_prio=0 tid=0x000002535c3fb000 nid=0xaaf0 waiting on condition [0x000000cd0c3ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
      at java.lang.Thread.sleep(Native Method)
      at TimeWaiting.run(ReadStackLog.java:20)
      at java.lang.Thread.run(Thread.java:750)
- `TimeWaitingThread` 调用了 `Thread.sleep()`,处于睡眠状态。
- 该线程会在指定的睡眠时间(例如 1000000 毫秒)后自动唤醒。

3. 区别对比

特性WAITINGTIMED_WAITING
等待时间无限期等待,直到被唤醒。有限期等待,时间到达后自动唤醒。
触发方法Object.wait()LockSupport.park()Thread.sleep()Object.wait(long)LockSupport.parkNanos()
是否需要唤醒需要其他线程显式唤醒(notify())。不需要显式唤醒,时间到达后自动唤醒。
典型场景线程等待某个条件(如生产者-消费者模型)。线程需要暂停一段时间(如定时任务)。

4. 代码示例

以下是一个简单的代码示例,展示 WAITINGTIMED_WAITING 的区别:

public class WaitingExample {
    public static void main(String[] args) {
        Object lock = new Object();

        // WAITING 示例
        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("WaitingThread 进入 WAITING 状态");
                    lock.wait(); // 无限期等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        waitingThread.start();

        // TIMED_WAITING 示例
        Thread timedWaitingThread = new Thread(() -> {
            try {
                System.out.println("TimeWaitingThread 进入 TIMED_WAITING 状态");
                Thread.sleep(5000); // 有限期等待 5 秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        timedWaitingThread.start();
    }
}
  • 输出
WaitingThread 进入 WAITING 状态
TimeWaitingThread 进入 TIMED_WAITING 状态
  • 分析
    • waitingThread 调用了 lock.wait(),进入 WAITING 状态,需要其他线程调用 lock.notify() 来唤醒它。
    • timedWaitingThread 调用了 Thread.sleep(5000),进入 TIMED_WAITING 状态,5 秒后会自动唤醒。

5. 总结

  • WAITING:线程无限期等待,直到被其他线程显式唤醒。
  • TIMED_WAITING:线程有限期等待,时间到达后自动唤醒。
  • 两者的主要区别在于等待的时间是否有限,以及是否需要显式唤醒。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值