【深入理解java虚拟机v3】代码清单4-8 线程等待演示代码

文章目录

简介

本篇目的是介绍JConsole查看线程的运行状态,下面的代码展示线程死循环、阻塞时,线程的状态

例子

如果说JConsole的“内存”页签相当于可视化的jstat命令的话,那“线程”页签的功能就相当于可视化的jstack命令了,遇到线程停顿的时候可以使用这个页签的功能进行分析。前面讲解jstack命令时提到
线程长时间停顿的主要原因有等待外部资源(数据库连接、网络资源、设备资源等)、死循环、锁等待等,代码清单4-8将分别演示这几种情况。

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author zzm
 */
public class ThreadDeadLockTestCase_1 {
    
    
    /**
     * 线程死循环演示
     */
    public static void createBusyThread() {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while (true)   // 第41行
                    ;
            }
        }, "testBusyThread");
        thread.start();
    }

    /**
     * 线程锁等待演示
     */
    public static void createLockThread(final Object lock) {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (lock) {
    
    
                    try {
    
    
                        lock.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }, "testLockThread");
        thread.start();
    }

    public static void main(String[] args) throws Exception {
    
    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.readLine();       //此时只有main线程运行,并且状态为Runnable,System.in让线程卡住了
        createBusyThread();    //只有输入字符后,才会执行到此
        br.readLine();    //可以理解为又卡住了
        Object obj = new Object();
        createLockThread(obj);    //只有再次输入字符后,才能执行到此
    }

}

JDK1.6环境运行,包括jconsole

  1. 运行程序运行,打开jconsole,如下图所示,此时只有main线程被创建,main线程阻塞因为正在等待用户输入

在这里插入图片描述

  1. 在“线程”页签中选择main线程,如图下图所示。堆栈追踪显示BufferedReaderreadBytes()方法正在等待System.in的键盘输入,这时候线程为Runnable状态,Runnable状态的线程仍会被分配运行时间,但readBytes()方法检查到流没有更新就会立刻归还执行令牌给操作系统,这种等待只消耗很小的处理器资源。

在这里插入图片描述

线程可运行(Runnable)状态解释 :线程对象A创建后,其他线程(比如main线程)调用了该对象A的start()方法。Runnable状态的线程A位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

所以说如果一个线程此刻的状态是Runnable时可能是有问题的,需要定位出Runnable状态的原因;readBytes()可以理解为阻塞等待,这就是原因。

3.在控制台随机输入一行字符,并按下回车键,此时创建线程testBusyThread。

接着监控testBusyThread线程,如图下图所示。testBusyThread线程一直在执行空循环,从堆栈追踪中看到一直在ThreadDeadLockTestCase_1.java代码的12行停留,12行的代码为while(true)。这时候线程为Runnable状态,而且没有归还线程执行令牌的动作,所以会在空循环耗尽操作系统分配给它的执行时间,直到线程切换为止,这种等待会消耗大量的处理器资源。

在这里插入图片描述

与main方法一样,此刻testBusyThread线程状态为Runnable状态,那么需要定位原因,最后发现是死循环导致的。后面可以总结下,哪些场景可以导致线程状态为Runnable状态

4.继续在控制台输入一行,按回车键,此时创建线程testLockThread

下图显示testLockThread线程在等待lock对象的notify()notifyAll()方法的出现,线程这时候处于WAITING状态,在重新唤醒前不会被分配执行时间。

testLockThread线程正处于正常的活锁等待中,只要lock对象的notify()或notifyAll()方法被调用,这个线程便能激活继续执行。

在这里插入图片描述

参考:使用Jconsole监控线程

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/108700398