专栏首页我是攻城师关于线程死锁问题

关于线程死锁问题

前言

死锁是多线程编程里面非常常见的一个问题,作为一个中高级开发者是必须掌握的内容,今天我们来学习一下死锁相关的知识。

什么是死锁

死锁指的是两个线程都需要获得锁,但是它们之间又成环引用,从而造成程序无限等待永远不会终止。

看下面一个demo示例:

public  void m1()throws Exception{

        //lock1
        synchronized (String.class){

            //lock2
            synchronized (Integer.class){

            }

        }


    }

    public  void m2()throws Exception{

        //lock1
        synchronized (Integer.class){

            //lock2
            synchronized (String.class){

            }

        }


    }

上面的示例中,有两个方法分别是m1和m2,假设这里有两个线程分别同时调用了m1和m2,那么这里就会有非常大的概率形成死锁,我们来简单推导一下它是如何发生的:

线程A调用了m1方法,拿到了String类的监视器,并进入了lock1的同步块,同时线程B调用了m2方法,拿到了Integer类的监视器,也进入了lock1的同步块,接下来线程A要进入lock2的同步块,但是它进不去因为synchronized方法只能有一个线程进入临界区,必须等待线程B释放了Integer监视器的锁,线程A才能继续执行,但线程B恰恰也需要线程A释放了String监视器才能释放Integer的锁,这样以来他们就形成了环路,谁都在等待对方释放锁,这样以来他们永远就会处于BLOCK状态,从而造成了死锁的问题。

这里有一个实际开发中典型的案例,在银行转账时候,比如有两个人A和B,A要转账给B,那么首先给A加锁,保证同一时候对A的操作永远只有一个人,然后调用A的扣款方法,接着要对B加锁,保证同一时候只有一个人操作B的账户,接着调用B的存款方法,看起很完美,但是如果同时B也对A转账,那么就可能形成死锁。

如何发现死锁

当程序发生死锁的时候,程序还处于运行状态,只不过线程被阻塞,可能应用程序会被卡住,这时候我们就需要通过一些手段来发现,常用的方法有:

(1)在win上打开cmd命令,输入jconsole,visualvm,jmc三者任意一个命令,都能打开相关的界面工具,在线程面板中我们可以非常轻而易举的找到死锁,如果找不到可以使用工具提供的死锁检测按钮来分析。

(2)在linux上也有一些方法,不管使用哪种方法,我们需要首先知道程序的进程id,这个可以执行jps -lvm命令来找到:

方法一使用kill -3 java_pid 这个命令并不会杀死Java程序而仅仅在终端到以标准输出流的方式,到处线程的dump信息。

方法二使用jdk自带的jstack命令,执行jstack java_pid 导出线程的dump信息之后,可以找到程序是否有死锁

如何避免死锁

关于避免死锁,这里有几个重要的实践经验:

(1)死锁的根源在于有多个同步锁存在,那么最好的解决方法就是没有锁的出现,就不会有死锁的问题或者使用Java并发包里面无锁的数据结构,如ConcurrentLinkedQueue,volatile,atom变量等,从而避免从根源上死锁问题。

(2)如果真的不能避免同步,必须使用锁,那么这里有一个重要的方法,就是保证两个线程锁的顺序是一致的,这样就不会出现死锁,比如第一个例子,如果改造成下面的代码就可以避免死锁:

// method1

m1 {

// lock1
sync(String.class){

//lock2
sync(Integer.class){

}

}

}

// method2

m2 {

// lock1
sync(String.class){

//lock2
sync(Integer.class){

}

}

}

知道这个规则之后,如银行的转账的例子,只需要根据账户的id排序,形成一个固定的顺序的嵌套锁,那么就可避免死锁的问题。

(3)如果仍然无法保证复杂的程序是否会有死锁的问题,那么我可以使用jdk5之后新的并发包里面的超时锁,这个不是避免问题, 但是可以减少死锁发生后影响,如果在一段时间内没有响应,就会超时自动释放自己持有的锁,从而在一定程度上减少死锁对应用的影响。

总结

本文主要介绍了Java里面关于线程死锁的问题,首先介绍了什么是死锁,然后讲了如何发现死锁,最后我们总结了如何避免死锁,这些内容对一个高级的开发者来说是必不可少的基本知识,掌握了这些将更加有助于编写具有更多鲁棒性的多线程程序。文中死锁的完整例子,已经上传到我的github上,这个项目包含了很多的Java相关的典型问题示例,感兴趣的朋友可以学习和了解一下。

https://github.com/qindongliang/Java-Note

本文分享自微信公众号 - 我是攻城师(woshigcs)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于线程死锁,活锁和饥饿问题

    上篇文章介绍了Java多线程里面最常见的死锁问题,其实除了死锁问题,这有两种问题虽然不常见,但是也需要我们了解一下,分别是线程活锁和线程饥饿。

    我是攻城师
  • 多线程基础知识了解一下

    作为一名优秀的攻城师,了解多线程的知识非常有必要,尤其在人工智能和机器学习的热潮下,如何提高程序或者算法的运行效率是非常有价值的一件事情。

    我是攻城师
  • 多线程基础知识了解一下

    作为一名优秀的攻城师,了解多线程的知识非常有必要,尤其在人工智能和机器学习的热潮下,如何提高程序或者算法的运行效率是非常有价值的一件事情。

    我是攻城师
  • 我来告诉你解决死锁的100种方法

    死锁是多线程编程或者说是并发编程中的一个经典问题,也是我们在实际工作中很可能会碰到的问题。相信大部分读者对“死锁”这个词都是略有耳闻的,但从我对后端开发岗位的面...

    本人秃顶程序员
  • SQLServer中的死锁的介绍

    简介 什么是死锁?      我认为,死锁是由于两个对象在拥有一份资源的情况下申请另一份资源,而另一份资源恰好又是这两对象正持有的,导致两对象无法完成操作,且...

    用户1217611
  • 操作系统产生死锁的原因和处理策略

    当进程需要以独占的方式访问资源时,可能会发生死锁(Deadlock)。死锁是指两个或以上进程因竞争临界资源而造成的一种僵局,即一个进程等待一个已经被占用且永不释...

    刘盼
  • 如何预防死锁发生

    1、尽量不要主动使用Lock table之类的语句,使用事务去代替此来操作。之前有过开发同学想向DBA申请lock table权限,这个行为本身是不妥当的。

    AsiaYe
  • 读写锁的死锁问题该如何预测?滴滴高级专家工程师这样解决

    桔妹导读:死锁是多线程和分布式程序中常见的一种严重问题。死锁是毁灭性的,一旦发生,系统很难或者几乎不可能恢复;死锁是随机的,只有满足特定条件才会发生,而如果条件...

    Python进阶者
  • 读写锁的死锁问题该如何预测?滴滴高级专家工程师这样解决

    桔妹导读:死锁是多线程和分布式程序中常见的一种严重问题。死锁是毁灭性的,一旦发生,系统很难或者几乎不可能恢复;死锁是随机的,只有满足特定条件才会发生,而如果条件...

    猿天地
  • 面试杂谈 - 死锁的四大条件与处理策略

    比如:两个线程A、B各自持有一个无法共享的资源,并且他们都需要获取对方现在持有的资源才能进行下一步,但是他们又必须等对方释放了才能去获取,于是A等待B,B也在等...

    acupt

扫码关注云+社区

领取腾讯云代金券