提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
jps+stack
jps:jdk提供的一个查看当前java进程命令
参数 | 含义 |
---|---|
jps | 端口号+进程名 |
jps -l | 端口号+应用程序main class的完整package名 or 应用程序的jar文件完整路径名 |
jps -v | jvm参数 |
jstack:查看jvm栈信息
jstack -l pid
tid指Java Thread id。nid指native线程的id。prio是线程优先级。
注意1:线程当前所处的一个状态 and 线程栈的起始地址
- waiting on condition:线程等待一个条件,此时该线程仍然占有Monitor。
- waiting for monitor entry:线程在EntrySet中等待,获取Monitor。说明此线程通过 synchronized(obj) {……} 申请进入了临界区,从而进入了下图1中的“Entry Set”队列,但该 obj 对应的 monitor 被其他线程拥有,所以本线程在 Entry Set 队列中等待。
- in Object.wait():当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() ,“ Wait Set”队列中线程才得到机会去竞争进入EntrySet
- runnable
注意2:线程自身的状态
- TIMED_WAITING (sleeping):指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态。sleep指线程处于睡眠状态。
- WAITING (on object monitor):等待状态,此时线程在WaitSet中
- RUNNABLE:运行状态
- BLOCKED (on object monitor):阻塞状态,此时线程在EntrySet中
注意3:线程执行过程中的堆栈信息
注意4:
Monitor对象内部,结合synchrnized关键字进行分析
同一时刻,只有一个线程成功占有Monitor(获取锁)
- EntrySet:是线程1进入synchronized临界区,但是monitor此时被别的线程占有,线程1阻塞,进入EntrySet等待竞争Monitor。【线程阻塞】
- WaitSet,线程1占有Monitor时,发现需要的资源无法获取,此时调用wait,进入WaitSet,等待被notify【线程等待】
实例分析
实例1:sleep
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
ok();
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
ok();
}
},"t2").start();
}
public synchronized static void ok(){
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
t1
locked <0x000000076acae290>:由于ok()被synchronized修饰,作为临界区,执行ok()之前,先要给临界区地址上锁
TIMED_WAITING (sleeping):t1执行了sleep(),交出CPU处于等待状态,等到时间到了之后,重新参与CPU竞争。sleep()过程中t1并不会释放锁。
waiting on condition:等待条件是时间
t2
waiting to lock <0x000000076acae290>:执行ok()之前,先要给临界区地址上锁,由于t1已经进行lock。此时t2处于waiting to lock 等待去加锁
BLOCKED (on object monitor):获取锁失败,阻塞
waiting for monitor entry:进入EntrySet中
实例2:死锁
public class Test {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
new Thread(new Runnable() {
@Override
public void run() {
try{
synchronized (o1){
System.out.println("t1 获取o1资源 开始执行");
Thread.sleep(2000);
synchronized (o2){
System.out.println("t1 获取o2资源 开始执行");
Thread.sleep(2000);
System.out.println("t1 释放o2资源 执行完毕");
}
System.out.println("t1 释放o1资源 执行完毕");
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (o2) {
System.out.println("t2 获取o2资源 开始执行");
Thread.sleep(2000);
synchronized (o1) {
System.out.println("t2 获取o1资源 开始执行");
Thread.sleep(2000);
System.out.println("t2 释放o1资源 执行完毕");
}
System.out.println("t2 释放o2资源 执行完毕");
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t2").start();
}
}
t1、t2在对不同地址lock之后,又想对对方已经锁住的地址进行加锁,造成死锁。
此时都处于阻塞状态BOLCKED
实例3:wait
public class Test {
public static Object object = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
ok();
}
},"t1").start();
}
public static void ok(){
synchronized (object){
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0x000000076acb1060> );当执行到 object.wait();,线程就放弃了 Monitor 的所有权,进入“Wait Set”队列(对应于 waiting on <0x000000076acb1060> )