Java多线程与JConsole实践:从线程状态到性能优化!!!

目录

一、前言

在操作系统和并发编程中,线程作为最小的执行单位,其生命周期中会经历多个不同的状态。理解这些状态及其转换非常重要,因为它们直接关系到程序的正确性和性能。本文将详细解析线程的各个状态及其转换关系。

二、JConsole 使用教程

JConsole 是一种 Java 监控和管理控制台工具,可以用于监视 Java 虚拟机(JVM)的性能和资源利用情况。它提供了一种图形化界面,可以实时查看 JVM 的运行状态、内存使用情况、线程活动、垃圾回收等信息,以及执行一些管理操作
1.启动JConsole

JConsole 是包含在 JDK 中的一个工具,因此首先要确保已经安装了 JDK。然后,找到jconsole.exe的位置然后 双击 jconsole.exe 启动 JConsole。

2.连接到 Java 进程

启动 JConsole 后,会弹出一个界面,显示所有正在运行的 Java 进程。选择要监控的 Java 进程,并点击连接按钮

我们先简单看一个代码:

java 复制代码
public class Tsleep {
    public static void main(String[] args)  {
        Thread t=new Thread(()->{
            try {
                Thread.sleep(3000);
                System.out.println("运行线程...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1");
        t.start();
        System.out.println("main....线程运行...");
    }
}

启动项目:然后打开jconsoel选中我们需要观察的线程。

3.本地连接

在本地进程中会展示出当前计算机所有正在运行的 Java 程序,只需选中双击进入,再点击"不安全的连接"即可进入到监听界面

然后我们就可以通过这个jconsole来更好的观察线程状态了。


二、线程的基本状态

2.1新建状态(New)

  • 当线程被创建时,线程处于新建状态。
  • 此时线程尚未开始执行,只是被操作系统或运行时环境识别为一个线程对象。
  • 例如:使用 new Thread() 创建一个线程实例,线程处于新建状态。

NEW: 安排了工作, 还未开始行动。

java 复制代码
public class Tsleep {
    public static void main(String[] args)  {
        Thread t=new Thread(()->{
            try {
                Thread.sleep(10000);
                System.out.println("运行线程...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1");
        System.out.println(t.getState());
        t.start();
    }
}

线程在start之前的状态。

可以看到NEW状态被打印出来了,这就是新建状态。

2.2就绪状态(Ready)

  • 线程被操作系统的调度器识别为可执行状态,等待 CPU 资源。
  • 此时线程已经被初始化并且具备了运行条件。
    当线程被创建(如 new Thread())后,线程处于 Ready 状态,但尚未开始执行。

例如:

java 复制代码
Thread thread = new Thread(() -> {
    System.out.println("Hello, Thread!");
});
// 此时线程处于 Ready 状态,但尚未调用 start()

2.3运行状态(Running)

线程获得 CPU 时间片,开始执行线程的 run() 方法。

这是线程执行任务的核心状态。

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作

java 复制代码
public class Dom12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
           while (true){
              //这里什么都不做
           }
        });
        t.start();
        //真正工作的状态
        System.out.println(t.getState());
    }
}

Ready 状态与 Running 状态的区别

特性 Ready 状态(就绪状态) Running 状态(运行状态)
CPU 资源 等待 CPU 资源 获得 CPU 资源,正在执行任务
执行状态 未执行 执行中
进入方式 调用 start()、从阻塞/等待恢复 调度器选择并分配 CPU 时间片
退出方式 获得 CPU 后进入 Running 状态 时间片用完或被中断,返回 Ready 状态

2.4 阻塞状态(Blocked)

  • 线程因等待某些资源(如 I/O、锁等)而暂停执行。
  • 阻塞状态的线程不在就绪队列中,直到等待的资源可用才会解除阻塞。
  • 例如:线程尝试获取一个已经被其他线程占用的锁时,会进入阻塞状态。
java 复制代码
package tset1;

public class Tsleep {
    public static void main(String[] args) throws InterruptedException {
        // 共享的锁对象
        Object lock = new Object();
// 线程 1
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread1: 已进入同步块");
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println("Thread1: 迭代 " + i);
                        Thread.sleep(3000); // 睡眠3000毫秒
                    }
                } catch (InterruptedException e) {
                    System.out.println("Thread1: 被中断");
                }
                System.out.println("Thread1: 已退出同步块");
            }
        },"T1");

// 线程 2
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread2: 已进入同步块");
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println("Thread2: 迭代 " + i);
                        Thread.sleep(3000); // 睡眠 3000 毫秒
                    }
                } catch (InterruptedException e) {
                    System.out.println("Thread2: 被中断");
                }
                System.out.println("Thread2: 已退出同步块");
            }
        },"T2");

// 启动两个线程
        thread1.start();
        thread2.start();

// 主线程等待两个子线程完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            System.out.println("Main: 在等待子线程完成时被中断");
        }
    }
}

代码解释

共享锁对象:

Object lock = new Object(); 定义了一个共享的锁对象,用于在多个线程之间同步。

线程创建:

创建了两个线程 thread1 和 thread2,它们都将尝试获取锁 lock。

在 synchronized (lock) 块内,线程将执行一个循环,睡眠500毫秒,模拟执行任务。

启动线程:

thread1.start(); 和 thread2.start(); 启动了两个线程,让它们竞争锁 lock。

主线程等待:

thread1.join();和thread2.join(); 确保主线程在等待两个子线程完成后再退出。

2.5. 等待状态(Waiting)

WAITING 状态是无限等待状态,直到被其他线程唤醒。

java 复制代码
public class Dom14 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (true){
                //System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new NullPointerException();
                }
            }
        });
        t.start();
        //join() 会出现WAITING状态  --死等
        t.join();
        System.out.println(t.getState());
    }
}

通过控制台观察不到,因为出现了死等所以,所以jconsole进行观察:

阻塞状态与等待状态的区别


2.6 等待状态(TIMED_WAITING)

TIMED_WAITING 状态是有限等待状态,线程会在指定时间后自动退出。

java 复制代码
public class Dom13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (true){
                //System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new NullPointerException();
                }
            }
        });
        t.start();
        Thread.sleep(100);
        //指定时间的阻塞 TIMED_WAITING 状态
        System.out.println(t.getState());
        //join(时间)也会进入TIMED_WAITING 状态
       t.join(100);
        System.out.println(t.getState());
    }
}

2.7终止状态(TERMINATED)

  • 线程完成执行或 debido a una interrupción external، 执行结束。
  • 一旦线程终止,其资源会被操作系统回收。

TERMINATED: 工作完成了.

java 复制代码
public class Dom11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            System.out.println("hello thread");
        });
        t.start();
        Thread.sleep(1000);
        //内核当中的线程已经结束了(工作结束了)
        System.out.println(t.getState());
    }
}

二、线程状态的转换

线程状态之间的转换是由操作系统的调度器和线程自身的行为决定的。以下是线程状态转换的常见情况:

⼤家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。
例子:(银行的例子)

刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;

当李四、王五开始去窗口排队,等待服务,就进⼊到 RUNNABLE 状态。该状态并不表示已经被银行工作⼈员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度;

当李四、王五因为⼀些事情需要去忙,例如需要填写信息、回家取证件、发呆⼀会等等时,进入BLOCKED 、 WATING 、 TIMED_WAITING 状态;如果李四、王五已经忙完,为 TERMINATED 状态。


  1. 就绪 → 运行
  • 操作系统的调度器选择一个就绪状态的线程,并分配 CPU 时间片。
  • 例如

    java 复制代码
    Thread thread = new Thread();
    thread.start();   
  1. 运行 → 就绪

    • 线程的 CPU 时间片用完,被调度器剥夺 CPU 使用权。

    - 例如:操作系统使用时间片调度,线程的时间片到期后会被暂停。

  2. 运行 → 阻塞

  • 线程在执行过程中等待某些资源(如 I/O 或锁),主动放弃 CPU。

例如:线程尝试获取一个被其他线程占用的锁:
java synchronized (lock) { //如果 lock 已被另一个线程占用,此线程将进入阻塞状态 }

  1. 运行 → 等待
  • 线程主动调用某些方法(如 sleep()),等待特定条件满足。

** 例如:**
java Thread.sleep(1000); //线程进入等待状态,等待 1 秒

  1. 阻塞/等待 → 就绪
  • 阻塞状态的线程等到所需资源可用(如锁被释放)。
  • 等待状态的线程等到超时或被其他线程唤醒。
  • 例如:

    java 复制代码
    // 阻塞状态的线程获取锁后会自动转移到就绪状态

三、线程安全与状态管理

在多线程编程中,线程状态的管理直接关系到程序的正确性和性能。以下是需要注意的事项:

  1. 避免长时间占用 CPU
  • 长时间占用 CPU 的线程会导致其他线程饥饿。
  • 建议使用 yield()sleep() 方法让出 CPU。
  1. 合理加锁和解锁
  • 锁的不当使用可能导致线程长时间阻塞。
  • 例如:在持有锁时避免执行耗时操作。
  1. 正确使用等待/通知机制
  • 使用 wait()notify() 方法可以更高效地管理线程间的协作。
  • 例如:实现生产者-消费者模式时,使用等待队列管理线程状态。

四、总结

在本文中,我们将通过JConsole这款强大的工具,深入探索Java多线程的核心知识。从线程的基本状态(如新建、就绪、运行、阻塞、等待和终止)到线程状态之间的转换机制,帮助开发者更好地理解和管理线程。通过实践性的教程,读者将学会如何利用JConsole监控和调试线程,从而优化应用程序的性能和稳定性。这篇文章适合Java开发人员和对并发编程感兴趣的学习者,旨在提供一份清晰易懂的指南,助力在多线程编程中游刃有余

如果您需要更深入的内容或具体案例,可以在留言区告诉我!

相关推荐
oliveira-time6 分钟前
ArrayList和LinkedList区别
java·开发语言
潮流coder9 分钟前
IntelliJ IDEA给Controller、Service、Mapper不同文件设置不同的文件头注释模板、Velocity模板引擎
java·ide·intellij-idea
yutian060612 分钟前
C语言中的宏
c语言·开发语言
码农飞哥15 分钟前
互联网大厂Java求职面试实战:Spring Boot与微服务场景深度解析
java·数据库·spring boot·安全·微服务·消息队列·互联网医疗
红衣小蛇妖17 分钟前
Python基础学习-Day23
开发语言·python·学习
孞㐑¥30 分钟前
Linux之进程控制
linux·开发语言·c++·经验分享·笔记
Akiiiira32 分钟前
【日撸 Java 300行】Day 14(栈)
java·开发语言
猴子请来的逗比48939 分钟前
tomcat与nginx之间实现多级代理
java·nginx·tomcat
一丝晨光42 分钟前
数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?
java·javascript·c++·rust·go·c·swift
意倾城1 小时前
浅说MyBatis-Plus 的 saveBatch 方法
java·mybatis