Java多线程 定位死锁--jstack

缺乏、安全感 2022-12-05 15:15 268阅读 0赞

文章目录

      • jstack定位死锁
      • 两个线程死锁分析
      • 多个线程死锁分析

jstack定位死锁

死锁发生的时候, 控制台什么也不打印, 无法进行排查. 这个时候就需要借助工具来进行定位死锁了.
工具之一就是jdk自带的命令jstack
在jdk的bin 目录中,可以看到是有jstack.exe这个应用程序的,配置好了jdk的环境变量后,那么就可以直接用jstack来分析死锁了.
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center

两个线程死锁分析

首先分析两个线程死锁的情况
运行之前的章节中, 两个线程必然发生死锁的代码demo

  1. package com.thread.deadlock;
  2. /** * 类名称:MustDeadLock * 类描述: 必定发生死锁的情况 * * @author: https://javaweixin6.blog.csdn.net/ * 创建时间:2020/9/8 7:55 * Version 1.0 */
  3. public class MustDeadLock implements Runnable {
  4. //标记位, 不同的线程根据标记位执行不同的代码
  5. int flag = 1 ;
  6. //两把锁
  7. static Object o1 = new Object();
  8. static Object o2 = new Object();
  9. public static void main(String[] args) {
  10. MustDeadLock r1 = new MustDeadLock();
  11. MustDeadLock r2 = new MustDeadLock();
  12. //给不同的线程, 设置不同的标记位
  13. r1.flag=1;
  14. r2.flag=2;
  15. Thread t1 = new Thread(r1);
  16. Thread t2 = new Thread(r2);
  17. t1.start();
  18. t2.start();
  19. }
  20. @Override
  21. public void run() {
  22. //打印出标记位
  23. System.out.println("flag = "+flag);
  24. if (flag == 1) {
  25. synchronized (o1) {
  26. try {
  27. //线程1持有锁o1, 并且等待500ms ,让线程2执行
  28. Thread.sleep(500);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. //线程1尝试去获取锁o2
  33. synchronized (o2) {
  34. System.out.println("线程1成功拿到两把锁");
  35. }
  36. }
  37. }
  38. if (flag == 2) {
  39. synchronized (o2) {
  40. try {
  41. //持有锁o2, 并且等待500ms ,让线程1执行
  42. Thread.sleep(500);
  43. } catch (InterruptedException e) {
  44. e.printStackTrace();
  45. }
  46. //线程2尝试去获取锁o1
  47. synchronized (o1) {
  48. System.out.println("线程2成功拿到两把锁");
  49. }
  50. }
  51. }
  52. }
  53. }

运行程序 , 可以看到控制台打印了1 和2之后, 再也没打印出东西了, 也没报错, 但是可以看到红色的按钮是亮着的,程序进入了阻塞的状态.
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 1
首先用jps 找出当前运行程序的PID, 可以看到我们自己运行的类MustDeadLock的PID为10052
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 2
输入jstack 10052 可以看到打印出来了线程间死锁的信息.
线程1持有0x000000076e471818这把锁, 需要0x000000076e471808这把锁.
线程0持有0x000000076e471808这把锁,需要0x000000076e471818这把锁.因此就造成了死锁.
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 3
把打印信息拖到最下面, 也可以看到 提示发现了一个死锁Found 1 deadlock
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 4

多个线程死锁分析

运行如下的多人转账的代码案例

  1. package com.thread.deadlock;
  2. import java.util.Random;
  3. /** * 类名称:MultiTransferMoney * 类描述: 多人转账发生死锁demo * * @author: https://javaweixin6.blog.csdn.net/ * 创建时间:2020/9/9 7:13 * Version 1.0 */
  4. public class MultiTransferMoney {
  5. //账户的总数量
  6. private static final int NUM_ACCOUNTS = 500;
  7. //每个账户初始的余额
  8. private static final int NUM_MONEY = 1000;
  9. private static final int NUM_ITERATIONS = 1000000;
  10. private static final int NUM_THREADS = 20;
  11. public static void main(String[] args) {
  12. Random random = new Random();
  13. //定义转账的账户数组
  14. TransferMoney.Account[] accounts = new TransferMoney.Account[NUM_ACCOUNTS];
  15. for (int i = 0; i < accounts.length; i++) {
  16. //给每个账户数组中的元素定初始值
  17. accounts[i] = new TransferMoney.Account(NUM_MONEY);
  18. }
  19. class TransferThread extends Thread {
  20. @Override
  21. public void run() {
  22. //每一个线程都进行随机的转账
  23. for (int i = 0; i < NUM_ITERATIONS; i++) {
  24. //随机获取转账方索引
  25. int fromAccount = random.nextInt(NUM_ACCOUNTS);
  26. //随机的获取收款方
  27. int toAccount = random.nextInt(NUM_ACCOUNTS);
  28. //随机获取转账金额
  29. int amount = random.nextInt(NUM_MONEY);
  30. //执行转账的方法
  31. TransferMoney.transferMoney(accounts[fromAccount],accounts[toAccount],amount);
  32. }
  33. }
  34. }
  35. //开启20个线程进行转账
  36. for (int i = 0; i < NUM_THREADS; i++) {
  37. new TransferThread().start();
  38. }
  39. }
  40. }

运行一段时间后, 程序进入阻塞状态了.
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 5
找出PID
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 6
执行 jstack 7464
可以看到如下的四个线程发生了死锁.
watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMjI5NjY5_size_16_color_FFFFFF_t_70_pic_center 7

发表评论

表情:
评论列表 (有 0 条评论,268人围观)

还没有评论,来说两句吧...

相关阅读

    相关 线实例与定位

    既然可以上锁,那么假如有2个线程,一个线程想先锁对象1,再锁对象2,恰好另外有一个线程先锁对象2,再锁对象1。在这个过程中,当线程1把对象1锁好以后,就想去锁对象2,但是不巧,