简介
本篇目的是介绍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
- 运行程序运行,打开jconsole,如下图所示,此时只有main线程被创建,main线程阻塞因为正在等待用户输入
- 在“线程”页签中选择
main
线程,如图下图所示。堆栈追踪显示BufferedReader
的readBytes()
方法正在等待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()方法被调用,这个线程便能激活继续执行。