学习 尚硅谷 宋红康 JVM从入门到精通 的学习笔记

概述

jstack(JVM Stack Trace)是用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪),线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合.
生成线程的快照的作用: 多线程在执行过程中可能会出现长时间停顿的问题,线程争抢资源的时候有的线程就需要等待同步监视器或者死锁,或者死循环,等等以上情况都会导致线程不正常的情况, 会出现长时间的停顿,要想知道是哪段代码导致的线程停顿的,这个时候就需要jstack指令了

image.png

image.png

语法


基本语法
option参数:-F
当正常输出的请求不被响应时,强制输出线程堆栈
option参数:-l
除堆栈外,显示关于锁的附加信息
option参数:-m
如果调用本地方法的话,可以显示C/C++的堆栈
option参数:-h
帮助操作

基本语法说明:
image.png
image.png
举例如下:
1.image.png
2.加-l参数:image.png
总结:
如果程序出现等待问题,可以使用该指令去查看问题所在,结果中也会提示你问题所在

演示

死锁问题排查

  1. /**
  2. * 演示线程的死锁问题
  3. */
  4. public class ThreadDeadLock {
  5. public static void main(String[] args) {
  6. StringBuilder s1 = new StringBuilder();
  7. StringBuilder s2 = new StringBuilder();
  8. new Thread(){
  9. @Override
  10. public void run() {
  11. synchronized (s1){
  12. s1.append("a");
  13. s2.append("1");
  14. try {
  15. Thread.sleep(100);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. synchronized (s2){
  20. s1.append("b");
  21. s2.append("2");
  22. System.out.println(s1);
  23. System.out.println(s2);
  24. }
  25. }
  26. }
  27. }.start();
  28. new Thread(new Runnable() {
  29. @Override
  30. public void run() {
  31. synchronized (s2){
  32. s1.append("c");
  33. s2.append("3");
  34. try {
  35. Thread.sleep(100);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. synchronized (s1){
  40. s1.append("d");
  41. s2.append("4");
  42. System.out.println(s1);
  43. System.out.println(s2);
  44. }
  45. }
  46. }
  47. }).start();
  48. try {
  49. Thread.sleep(1000);
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. }
  53. new Thread(new Runnable() {
  54. @Override
  55. public void run() {
  56. Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();//追踪当前进程中的所有的线程
  57. Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();
  58. for(Map.Entry<Thread, StackTraceElement[]> en : entries){
  59. Thread t = en.getKey();
  60. StackTraceElement[] v = en.getValue();
  61. System.out.println("【Thread name is :" + t.getName() + "】");
  62. for(StackTraceElement s : v){
  63. System.out.println("\t" + s.toString());
  64. }
  65. }
  66. }
  67. }).start();
  68. }
  69. }

说明,第一个线程先获取s1锁,再获取s2锁 , 第二个线程先获取s2锁,再获取s1锁,这样就很容易出现死锁了

用命令排查

启动上面的代码

image.png

image.png

这是就打印了线程的相关信息了,

箭头标识的这两个线程出现了阻塞状态,就是因为死锁了,
image.png

然后里面就显示deadlock了.
image.png

线程睡眠问题排查

  1. public class TreadSleepTest {
  2. public static void main(String[] args) {
  3. System.out.println("hello - 1");
  4. try {
  5. Thread.sleep(1000 * 60 * 10);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. System.out.println("hello - 2");
  10. }
  11. }

上面的代码会睡眠10分钟.让代码不执行

用命令排查

启动上面的main方法

image.png

可以看到main线程是time waiting状态了.
image.png

多线程同步问题

  1. /**
  2. * 演示线程的同步
  3. */
  4. public class ThreadSyncTest {
  5. public static void main(String[] args) {
  6. Number number = new Number();
  7. Thread t1 = new Thread(number);
  8. Thread t2 = new Thread(number);
  9. t1.setName("线程1");
  10. t2.setName("线程2");
  11. t1.start();
  12. t2.start();
  13. }
  14. }
  15. class Number implements Runnable {
  16. private int number = 1;
  17. @Override
  18. public void run() {
  19. while (true) {
  20. synchronized (this) {
  21. if (number <= 100) {
  22. try {
  23. Thread.sleep(500);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println(Thread.currentThread().getName() + ":" + number);
  28. number++;
  29. } else {
  30. break;
  31. }
  32. }
  33. }
  34. }
  35. }

上面代码说明,创建两个Number线程 ,然后去给number变量自增,其中锁对象是this, 这样就是同步

image.png