简介

如果我们在程序中遇到线程死锁的时候,该怎么去解决呢?

本文将会从一个实际的例子出发,一步一步的揭开java问题解决的面纱。

死锁的代码

写过java多线程程序的人应该都知道,多线程中一个很重要的事情就是状态的同步,但是在状态同步的过程中,一不小心就有可能会导致死锁的问题。

一个最简单的死锁情况就是thread1占有资源1,然后又要去获取资源2. 而thread2占有资源2,又要去获取资源1的情况。

举个具体的例子:

public class TestDeadLock {
public static Object lock1= new Object();
public static Object lock2= new Object();
public static void main(String[] args) {
Runnable runnable1= ()-> {
System.out.println("in lock1");
synchronized(lock1){
System.out.println("Lock1 lock obj1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock2){
System.out.println("Lock1 lock obj2");
}
}
}; Runnable runnable2= ()-> {
System.out.println("in lock2");
synchronized(lock2){
System.out.println("Lock2 lock obj2");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock1){
System.out.println("Lock2 lock obj1");
}
}
}; Thread a = new Thread(runnable1);
Thread b = new Thread(runnable2);
a.start();
b.start();
}
}

我们运行上面的代码:

in lock1
Lock1 lock obj1
in lock2
Lock2 lock obj2

发送了锁循环等待的情况,程序执行不下去了,发送了死锁。

control+break命令

在代码很简单的情况下,我们很容易就能分析出来死锁的原因,但是如果是在一个非常庞大的线上项目的时候,分析代码就没有那么容易了。

怎么做呢?

今天教给大家一个方法,使用control+break命令。

control+break在linux表示的是Control+backslash,而在Windows下面就是Control+Break按钮。

当然,还有一个更加通用的就是使用:

kill -QUIT pid命令。

我们用jps命令获取到执行java程序的进程id,然后执行kill -QUIT命令。

执行完毕,我们会发现运行的java进程会输出一些额外的日志,这些额外的日志就是我们找出死锁的关键因素。

注意,这个kill命令并不会终止程序的运行。

输出的内容比较多,我们一部分一部分的讲解。

Full thread dump

日志的第一部分就是Full thread dump,包含了JVM中的所有线程的状态信息。

我们看一下我们代码中的两个关键线程信息:

"Thread-0" #13 prio=5 os_prio=31 cpu=4.86ms elapsed=230.16s tid=0x00007fc926061800 nid=0x6403 waiting for monitor entry  [0x0000700008d6a000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21)
- waiting to lock <0x0000000787e868f0> (a java.lang.Object)
- locked <0x0000000787e868e0> (a java.lang.Object)
at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832) "Thread-1" #14 prio=5 os_prio=31 cpu=4.32ms elapsed=230.16s tid=0x00007fc924869800 nid=0x6603 waiting for monitor entry [0x0000700008e6d000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36)
- waiting to lock <0x0000000787e868e0> (a java.lang.Object)
- locked <0x0000000787e868f0> (a java.lang.Object)
at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)

上面的输出列出了线程名字,线程的优先级,cpu时间,是否是daemon线程,线程ID,线程状态等有用的信息。

看到上面的输出,我们看到两个线程都是处于BLOCKED状态,都在等待object monitor。

还记得线程的几个状态吗? 我们再来复习一下。

死锁检测

接下来的部分就是我们最关心的死锁检测了。

Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x00007fc926807e00 (object 0x0000000787e868f0, a java.lang.Object),
which is held by "Thread-1" "Thread-1":
waiting to lock monitor 0x00007fc926807f00 (object 0x0000000787e868e0, a java.lang.Object),
which is held by "Thread-0" Java stack information for the threads listed above:
===================================================
"Thread-0":
at com.flydean.TestDeadLock.lambda$main$0(TestDeadLock.java:21)
- waiting to lock <0x0000000787e868f0> (a java.lang.Object)
- locked <0x0000000787e868e0> (a java.lang.Object)
at com.flydean.TestDeadLock$$Lambda$14/0x0000000800b69840.run(Unknown Source)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)
"Thread-1":
at com.flydean.TestDeadLock.lambda$main$1(TestDeadLock.java:36)
- waiting to lock <0x0000000787e868e0> (a java.lang.Object)
- locked <0x0000000787e868f0> (a java.lang.Object)
at com.flydean.TestDeadLock$$Lambda$15/0x0000000800b69c40.run(Unknown Source)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832) Found 1 deadlock.

上面的日志我们可以很明显的看出来,两个线程分别获得了对方需要的锁,所以导致了死锁。

同时还详细的列出了thread stack的信息,供我们分析。

如果我们添加了参数-XX:+PrintConcurrentLocks,还会输出各个线程的获得的concurrent lock信息。

Heap信息

最后一部分是Heap的统计信息:

Heap
garbage-first heap total 133120K, used 3888K [0x0000000780000000, 0x0000000800000000)
region size 1024K, 4 young (4096K), 0 survivors (0K)
Metaspace used 1122K, capacity 4569K, committed 4864K, reserved 1056768K
class space used 108K, capacity 412K, committed 512K, reserved 1048576K

如果我们添加了-XX:+PrintClassHistogram命令,还可以额外的输出class直方图统计信息。

总结

上面就是使用Control+Break命令来分析java死锁问题的具体例子,希望大家能够喜欢。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jvm-diagnostic-control-break/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

troubleshoot之:用control+break解决线程死锁问题的更多相关文章

  1. Python多线程,线程死锁及解决,生产者与消费者问题

    1.Thread类 普通调用 t = Thread(target=test, args=(i,)) # test为目标函数名, 若函数需要参数将其以元组形 # 式赋给args, 若无参数可不写 t.s ...

  2. 尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁

    (最终采用的是方法4) 问题详情见:.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长 看看在 Linux 与 Windows 上发生线程死锁的后果. Linux: Microsoft ...

  3. 线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法~ 线程间操作无效: 从不是创建控件“Control Name'”的线程访问它问题的解决方案及原理分析

    看两个例子,一个是在一个进程里设置另外一个进程中控件的属性.另外一个是在一个进程里获取另外一个进程中控件的属性. 第一个例子 最近,在做一个使用线程控制下载文件的小程序(使用进度条控件显示下载进度)时 ...

  4. java——多线程的实现方式、三种办法解决线程赛跑、多线程数据同步(synchronized)、死锁

    多线程的实现方式:demo1.demo2 demo1:继承Thread类,重写run()方法 package thread_test; public class ThreadDemo1 extends ...

  5. CSerialPort串口类最新修正版(解决关闭死锁问题)2014-01-11

    这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等.Remon提供的串口类网址为:http://codeguru.earthweb.com/network/ ...

  6. JDK中ThreadDump诊断Java代码中的线程死锁问题

    多线程的死锁..死锁不是死了而是线程互相等待... 在项目中可能就是在几十万行的代码中存在一个死锁的问题,如何发现这个问题并且解决这个问题. JavaJDK为我们提供了一个诊断工具叫做ThreadDu ...

  7. .NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长

    一个 asp.net core 站点,之前运行在Linux 服务器上,运行一段时间后有时站点会挂掉,在日志中记录很多“EMFILE too many open files”的错误: Microsoft ...

  8. java笔记--关于线程死锁

    关于线程死锁 什么是死锁: 在编写多线程的时候,必须要注意资源的使用问题,如果两个或多个线程分别拥有不同的资源, 而同时又需要对方释放资源才能继续运行时,就会发生死锁. 简单来说:死锁就是当一个或多个 ...

  9. Lock锁方式解决线程安全问题

    在JDK5.0之后新增加了一种更强大的线程同步机制---通过显示定义同步锁来实现线程同步解决线程安全问题.同步锁使用Lock对象充当. java.util.concurrent.locks.lock接 ...

随机推荐

  1. Activiti工作流--分布式实现方案

    一.运行环境 以下所有的描述都是基于Activiti的5.20.0.1版本 public interface ProcessEngine extends EngineServices { /** th ...

  2. 蜂鸟E203系列——Linux调试(GDB+Openocd)

    欲观原文,请君移步 本文基于文章<蜂鸟E203系列--利用 Hbrid-E-SDK 环境开发程序> GDB 简介 GDB(GNU Project Debugger),是 GNU 工具链中的 ...

  3. bzoj3892[Usaco2014 Dec]Marathon*

    bzoj3892[Usaco2014 Dec]Marathon 题意: 在二维平面上有N个点,从(x1,y1)到(x2,y2)的代价为|x1-x2|+|y1-y2|.求从1号点出发,按从1到N的顺序依 ...

  4. Linux下一只五颜六色的「猫」

    大家好,我是良许. 有使用过 Linux 系统的小伙伴,肯定会使用过 cat 这个命令.当然,在 Linux 下,此猫非彼猫,这里的 cat 并不代表猫,而是单词 concatenate 的缩写. c ...

  5. ffmpeg源码编译环境搭建

    ffmpeg是视频开发最常用到的开源软件,FFmpeg功能强大,用途广泛,提供几乎所有你能够想到的与视频开发相关的操作,许多商业软件都以ffmpeg为基础进行开发定制. FFmpeg: FFmpeg ...

  6. 如何用 Python 做自动化测试【进阶必看】

    一.Selenium 环境部署 1. window 环境部署 1.1 当前环境Win10 64 位系统:Python3.6.2(官方已经更新到了 3.6.4) 官方下载地址:https://www.p ...

  7. 【计网】图解HTTP常见知识点总结

    目录 目录 目录 初识TCP/IP TCP/IP协议族4层模型 初识HTTP 请求和响应 HTTP报文 HTTP状态码 HTTP报文首部 其他的首部字段 确保WEB安全的HTTPS HTTPS工作原理 ...

  8. STL源码剖析:迭代器

    准备知识 什么是迭代器? 迭代器是链接容器和算法的桥梁,所有的算法都通过迭代器操作容器中的数据 迭代器是一种智能指针,最重要的操作符重载就是operator*,operator-> 迭代器的实现 ...

  9. [日常摘要] -- zookeeper篇

    概览 设计目标 是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用 简介 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于Z ...

  10. ELK5.6.4+Redis+Filebeat+Nginx(CentOS7.4)

    下载地址: Elasticsearhc: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.4.tar.gz ...