博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中死锁的定位与修复
阅读量:5283 次
发布时间:2019-06-14

本文共 2847 字,大约阅读时间需要 9 分钟。

死锁应该可以说是并发编程中比较常见的一种情况,可以说如果程序产生了死锁那将会对程序带来致命的影响;所以排查定位、修复死锁至关重要;

  我们都知道死锁是由于多个对象或多个线程之间相互需要对方锁持有的锁而又没有释放对方所持有的锁,导致双方都永久处于阻塞状态

84976-20180618190234476-1199515989.png

  如上图所示,线程1持有对象1的锁、线程2持有对象2的锁,持此线程1又想去获取对象2对象锁、线程2想获取对象1对象锁,此时由于双方都没有获取到想要的锁,任务没完成所以也没释放锁,导致一直僵持呢,于是阻塞、产生死锁;

死锁检测

  需要检测死锁肯定要先有死锁出现,下面的demo模拟了一个死锁的产生;

public class DeadlockDemo extends Thread {private BaseObj first;private BaseObj second;public DeadlockDemo(String name, BaseObj first, BaseObj second) {    super(name);    this.first = first;    this.second = second;}public void reentrantLock() throws InterruptedException {    first.lock();    System.out.println(String.format("%s 持有:%s 对象锁,等待获取:%s对象锁", this.getName(), first, second));    second.lock();    first.unlock();    second.unlock();}@Overridepublic void run() {    try {        reentrantLock();    } catch (Exception e) {        e.printStackTrace();    }}public static void main(String[] args) throws InterruptedException {    ObjOne one = new ObjOne();    ObjTwo two = new ObjTwo();    DeadlockDemo thread1 = new DeadlockDemo("Thread1", one, two);    DeadlockDemo thread2 = new DeadlockDemo("Thread2", two, one);    thread1.start();    thread2.start();    thread1.join();    thread2.join();} }

  运行上面的demo将看到程序被阻塞了,没法结束运行;只看到如下运行结果:

Thread1 持有:objOne 对象锁,等待获取:objTwo对象锁 Thread2 持有:objTwo 对象锁,等待获取:objOne对象锁

84976-20180618190310636-1363754327.png

  这demo没法结束运行就是由于产生了死锁,两个线程都在相互对待获取对方所持有的对象锁;

  这时候要解决问题就需要找出哪里出现了死锁,通过代码走查通常不容易发现死锁,当然我们这程序很容易发现,因为我们刻意产生的死锁;所以就需要工具来检测死锁,这里可用的工具主要有:jconsole、jvisualvm、jstack等,这些工具其实都是jdk自带的,用法都很类似;

  这里使用jvisualvm来检测当前的demo程序是否产生了死锁;打开jvisualvm连接到当前的应用程序即可看到程序的监控信息,如内存、CPU、性能、GC等等;打开进入线程的tab项查看程序的线程信息,这里很明显的就看到了提示该程序被检测除了死锁!

84976-20180618190425389-858797803.png

  点击 线程Dump可以看到线程的堆栈信息,从中可以看到线程的详细信息,并定位死锁;

84976-20180618190434781-1826469909.png
  从上图可以看到线程产生死锁的原因,Thrad2是等待Thread1、Thread1是等待Thread1, 从下图的堆栈信息即可定位死锁产生的位置;
84976-20180618190444776-1209116104.png

死锁扫描

  除了发现程序出现问题后我们去扫描死锁外,我们还可以实时的去扫描程序用于发现程序中是否存在死锁;

  JDK提供了MXBean Api可用于扫描程序是否存在死锁,ThreadMXBean提供了findDeadlockedThreads()方法,可以用于找到产生死锁的线程;这里在上面的demo程序中添加一个方法用于扫描死锁,虽然这种方法可以扫描到死锁但是由于每次都对线程打快照对程序性能会有比较大的影响,所以慎用;

public static void scanDeadLock() {    ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();    Runnable runnable = () -> {        long[] ids = mxBean.findDeadlockedThreads();        System.out.println("扫描死锁...");        if (ids != null) {            ThreadInfo[] threadInfos = mxBean.getThreadInfo(ids);            for (ThreadInfo threadInfo : threadInfos) {                System.out.println(threadInfo);            }        }    };    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5, Executors.defaultThreadFactory());    executorService.scheduleAtFixedRate(runnable, 1, 5, TimeUnit.SECONDS);}

84976-20180618190459668-1343062446.png

避免死锁

  解决死锁最好的方法就是避免死锁了,比如上面的demo我们可以把直接使用无参数的lock()方法换为使用tryLock方法,tryLock还可以指定获取锁超时时间,到了超时时间还没获得到锁就会放弃获取锁,当然还有其它方法可以避免死锁;

  1、避免使用多个锁、长时间持有锁;
  2、设计好多个锁的获取顺序
  3、使用带超时的获取锁方法

84976-20180618190508222-72830683.png

文章首发地址:

http://www.solinx.co/archives/1196

转载于:https://www.cnblogs.com/softlin/p/9195830.html

你可能感兴趣的文章
线程池的概念
查看>>
Java 序列化
查看>>
Java 时间处理实例
查看>>
Java 多线程编程
查看>>
Java 数组实例
查看>>
mysql启动过程
查看>>
2017前端面试题总结
查看>>
SWIFT国际资金清算系统
查看>>
站立会议第四天
查看>>
利用AMPScript获取Uber用户数据的访问权限
查看>>
Mysql 数据库操作
查看>>
转:linux终端常用快捷键
查看>>
UVa 11059 最大乘积
查看>>
数组分割问题求两个子数组的和差值的小
查看>>
《深入分析Java Web技术内幕》读书笔记之JVM内存管理
查看>>
161017、SQL必备知识点
查看>>
kill新号专题
查看>>
MVC学习系列——Model验证扩展
查看>>
Suite3.4.7和Keil u3自带fx2.h、fx2regs.h文件的异同
查看>>
打飞机游戏【来源于Crossin的编程教室 http://chuansong.me/account/crossincode 】
查看>>