文章目录
- (一) 应用场景
- 线程五种状态
- (二) jvisualvm 监控
- 1. 线程等待
- 2. 线程死循环
- 3. 线程活锁等待
- 4. 线程死锁
- (三) jstack 监控
- 介绍
- 使用
(一) 应用场景
- 当系统陷入hung状态的时候(系统不输出报错,也不干活)
- 看日志只能看到大致层面的报错,但是有些线程内部的问题,很多都无法体现在日志上,就需要使用jdk自带的工具去定位错误的位置
- 有时候系统性能不是很好,需要监控一些内存,堆栈区的使用情况
线程五种状态
- 新建:new(时间很短)
- 运行:runnable
- 等待:waitting(无限期等待),timed waitting(限期等待)
- 阻塞:blocked
- 结束:terminated(时间很短)
(二) jvisualvm 监控
- 用如下代码模拟 线程等待,线程死循环和线程活锁等待
public class MonitoringTest {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("-----线程等待-----");
br.readLine();
System.out.println("-----线程进入死循环-----");
createBusyThread();
System.out.println("-----线程等待-----");
br.readLine();
Object obj = new Object();
System.out.println("-----线程锁等待");
createLockThread(obj);
}
/**
* 线程死循环
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true);
}
}, "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();
}
}
1. 线程等待
-
main
线程等待用户输入,可以看到main线程还是处于Runnable
状态 -
Runnable
状态的线程会被分配运行时间,但readBytes方法检查到流没有更新时候就会归还执行令牌,这种等待只消耗很小的CPU资源。
2. 线程死循环
-
testBusyThread
线程一直执行空循环,从状态中看到代码停留在38行也就是while(true)
这一行,此时线程为Runnable状态,而且没有归还线程令牌的动作会在空循环用尽全部执行时间直到线程切换,这种等待比较消耗CPU资源。
3. 线程活锁等待
-
testLockThread
线程中的锁 , 需要等这lock对象的notify
或者notifyAll
方法激活线程,就能继续执行,这属于活锁等待. 后面还有死锁演示. - 此时线程处于
waiting
状态,在被唤醒之前不会被分配执行时间。
4. 线程死锁
- 如下这段代码:两个线程分别计算1+2以及2+1的值,由于两个线程导致死锁的概率很小,需要尝试运行多次才能看到效果。所以用for循环开了200个线程。
public static void main(String[] args) throws Exception {
System.out.println("-----线程死锁-----");
for (int i = 0; i < 100; i++) {
new Thread(new SynAddRunalbe(1, 2)).start();
new Thread(new SynAddRunalbe(2, 1)).start();
}
}
static class SynAddRunalbe implements Runnable {
int a, b;
public SynAddRunalbe(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
}
- 造成死锁的原因是
Integer.valueOf()
方法基于减少对象创建和节省内存的考虑,[-128,127]之间的数字会被缓存,当valueOf()方法传入参数在这个范围内,将直接返回缓存中的对象。也就是说,代码调用了200次Integer.valueOf()
一共就返回两个不同的对象。 - 假如在某个线程的
synchronized
块之间发生了一次线程切换,那就会出现线程A等待线程B持有的Integer.valueOf(1)
,线程B又等待线程A持有的Integer.valueOf(2)
,故而出现死锁。 - 点击
jvisualvm
中的检测死锁,可以看到有哪些处于死锁中的线程,状态处于Blocked
,也能看到具体是那行出现了死锁.
(三) jstack 监控
介绍
- 有时候远程服务器上并没有开启远程jmx端口,又不能重启服务,这时候就需要线上环境中使用命令行去监控,这时候就要用到
jstack
- java程序崩溃生成core文件后,也可以用jstack来获得core文件中的堆栈信息
- jstack工具还可以附属到正在运行的java程序中,看到目前运行着的程序的堆栈信息, 如果现在运行的java程序呈现hung的状态(
hung就是挂起、假死的意思,这里的外在表现就是不报错也不干活
),jstack是非常有用的。
使用
- jps命令查看有哪些运行中的java程序, 获得到pid
- jstack pid 查看该进程的堆栈信息, 可以看到一个个线程的运行状态
- 例如上面出现的死锁, 使用就stack也可以定位到具体的位置.