jstack简单使用,定位死循环、线程阻塞、死锁等问题

版权声明:本文为博主原创文章,如果转载请务必注明出自本博客:qq_2300688967,否则追究责任。 https://blog.csdn.net/qq_2300688967/article/details/83502044

当我们运行java程序时,发现程序不动,但又不知道是哪里出问题时,可以使用JDK自带的jstack工具去定位;

废话不说,直接上例子吧,在window平台上的;

一、死循环

package software.architect.OtherAnalyzer.main;

public class EndlessLoop {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		while(true) {
			
		}
	}

}

1,先运行以上程序,程序进入死循环;

2,打开cmd,输入jps命令,jps很简单可以直接显示java进程的pid,如下为13468:

3, 输入jstack 13468命令,找到跟我们自己代码相关的线程,如下为main线程,处于runnable状态,在main方法的第7行,也就是我们死循环的位置:

二、Object.wait()情况

package software.architect.OtherAnalyzer.main;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ObjectWait {
	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		executorService.execute(new ThreadTest());
	}

}


class ThreadTest implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(this) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

通过jps找到进程的id,然后使用jstack查看进程的状态,可以看到有一个线程处于waiting状态,且是在OtherAnalyzer类的main方法调用的ThreadTest类的run方法的第26行,正是我们调用wait方法的地方,说明该线程目前还没等到notify

三、死锁

package software.architect.OtherAnalyzer.main;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class deadlock {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Object obj1 = new Object();
        Object obj2 = new Object();

        ExecutorService ex = Executors.newFixedThreadPool(10);
        // 起10个线程
        for (int i = 0; i < 10; i++) {
            int order = i%2==0 ? 1 : 0;
            ex.execute(new ThreadTask(order, obj1, obj2));
        }
	}
}

class ThreadTask implements Runnable {
	
	private Object obj1;
	
	private Object obj2;
	
	private int order;
	
	public ThreadTask(int order, Object obj1, Object obj2) {
		this.order = order;
		this.obj1 = obj1;
		this.obj2 = obj2;
	}
	
	public void test1() throws InterruptedException {
		synchronized(obj1) {
			Thread.yield();
			synchronized(obj2) {
				System.out.println("test1.............");
			}
		}
	}

	public void test2() throws InterruptedException {
		synchronized(obj2) {
			Thread.yield();
			synchronized(obj1) {
				System.out.println("test2.............");
			}
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			try {
				if(this.order == 1) {
					this.test1();
				}else {
					this.test2();
				}
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
}

运行上面程序,然后执行jps命令后,看到如下,deadlock的进程id为9792 

执行jstack 9792,可以看到如下结果,说明了相关的被锁线程和分别等待的对象被哪一个线程所拥有:

四、等待IO

package software.architect.OtherAnalyzer.main;

import java.io.IOException;
import java.io.InputStream;

public class WaitIO {

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		 InputStream is = System.in;
	        int i = is.read();
	        System.out.println("exit。");
	}
}

同样,可以通过jstack在相关线程中查看到等待io的线程的信息 

五、sleep

package software.architect.OtherAnalyzer.main;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SleepThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		executorService.execute(new SleepThreadTest());
		
	}

}

	
class SleepThreadTest implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(this) {
			try {
				Thread.sleep(6000000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

通过jstack查看,状态如下:

附:

1,Thread.yield( )方法:
       使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
       Java线程中有一个Thread.yield( )方法,很多人翻译成线程让步。顾名思义,就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。

       打个比方:现在有很多人在排队上厕所,好不容易轮到这个人上厕所了,突然这个人说:“我要和大家来个竞赛,看谁先抢到厕所!”,然后所有的人在同一起跑线冲向厕所,有可能是别人抢到了,也有可能他自己有抢到了。我们还知道线程有个优先级的问题,那么手里有优先权的这些人就一定能抢到厕所的位置吗? 不一定的,他们只是概率上大些,也有可能没特权的抢到了。

2,sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。两者的区别在于:

        sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。

        当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态。

       sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。

        sleep()方法比yield()方法具有更好的可移植性。不能依靠yield()方法来提高程序的并发性能。对于大多数程序员来说,yield()方法的唯一用途是在测试期间人为地提高程序的并发性能,以帮助发现一些隐藏的错误。

3, suspend和sleep其实本质是一样的,但是suspend之后还要认为的恢复线程(resume),而resume操作有可能就会被阻塞而造成死锁,就像上面的例子,死锁的必然条件都成立了;而sleep却是等待时候后自动回复,也就是说在这中间不会涉及到其他操作。这样就避免了死锁的产生(恢复操作遭到阻塞)

猜你喜欢

转载自blog.csdn.net/qq_2300688967/article/details/83502044
今日推荐