多线程学习之常见死锁问题

多线程开发避不开锁,而锁又避不开死锁问题,所以弄清楚死锁问题才能开发出好的多线程程序。

死锁出现原因与解决方法

在多线程开发中,都是通过加锁来保证线程安全,但是过度的使用锁可能导致死锁。在数据库系统中有对死锁的检测并从死锁中恢复功能,一个事务可能会获取多个锁,当多个事务发生死锁,会选择牺牲一个事务,释放这个事务的所有锁,然后重新执行。而Java程序无法从死锁中恢复过来,因此在设计时一定要排除那些导致死锁出现的条件。

获取锁顺序造成死锁

而最简单的死锁是线程A持有资源M并想获取资源N,线程B持有资源N并想获取M,造成线程互相等待出现死锁。

造成这种死锁的原因是获取锁顺序不一致。所以解决方案是所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。

动态锁造成死锁

现在有一个转账程序,当A转账给B时,先获取A的锁在获取B锁,表面上似乎没有问题,但是如果同时又出现B转账给A,所以它会先锁B在锁A,造成死锁。这种不是代码层面的锁而是数据层面不容易发现。

要解决这种问题的根本办法还是加锁的顺序问题,现在的程序是固定的先锁定转账方在锁定收款方,由于用户身份的转变造成的加锁顺序不一致,可以在用户层面判断加锁顺序,比如根据用户id来判断加锁顺序而不是用户身份,这样所有的用户的加锁顺序就一致了,就解决了死锁问题。

相互引用造成死锁

一个synchronized修饰的方法内部调用了外部方法,而外部方法也是synchronized方法,虽然表面调用方法只有一个锁,实际上要执行这个方法却加了两个锁,实例如下图:


一个线程调用A对象的setA方法,另外一个线程调用B对象的println方法就有可能出现死锁情况,虽然表面上他们都没有加锁,但是两个方法实际上已经出现了顺序不一致的加锁方式

如果在持有锁的时候调用外部方法,那么将出现活跃性问题,在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。最好的办法是在调用某个方法时不需要持有锁,这种调用叫开放调用。上面示例的解决方法如下图:


通过改造两个方法在调用外部方法的时候都没有在持有锁,这样就不会出现死锁了

资源死锁

除了加锁导致的死锁问题,还有一些并不是因为加锁而导致的,比如通过线程池和信号量来限制对资源的使用,这些被限制的行为可能导致资源死锁。

比如一个线程获取线程池A的线程并等待获取线程池B的线程,另外一个线程获取了线程池B的线程在等待获取线程池A的线程,也会出现资源死锁。

还有一种是线程饥饿死锁线程池的一个线程提交了一个任务到当前线程池也可能造成,比如线程池只有一个线程,这个线程往当前线程池提交了一个任务并且在等待执行结果,由于当前线程占用了线程池的资源,所以提交的任务会处于等待中,而当前线程也在阻塞等待提交的任务执行完成,最后造成死锁。

解决死锁

最好的结局方法是系统中的程序最多只获取一个锁,那么就不会产生死锁。如果要获取多个锁,那么就要严格限制获取锁的顺序,并形成获取锁的文档。

还有一种方法是定时锁,Lock类的tryLock可以指定超时时间,如果超时没获取就失败。

其他原因死锁

如果一个获取了某个锁的线程优先级很低或者处于一个死循环,那么就将导致其他线程不能获取到锁,也会出现死锁,所以在平时的开发中不推荐设置线程的优先级

还有一种情况是活锁,提交的一个任务到队列,在执行的时候报错,可以会为了保护机制再次放到队列里面,等待下次执行,但是可能判断不完全使得这个任务不管运行多少次都是相同的结果,最终这个任务始终得不到最终的执行。

还有一种情况是当两个相互协作的线程,他们在发现对象在执行某些操作的时候会休息一下,等对方先执行,等一段时候后再次执行。这种协作机制会出现两个协作线程同时发现对方正在执行,同时休息相同的时间,然后当再次运行的时候发现对方仍然在执行,然后都会再次休眠,这样就会出现相互等待,任务一直执行不成功。这种解决方法是他们的休眠时间随机,这样就避免了大部分的阻塞情况。

总结

学习多线程开发避免不了锁,而学习锁也就避免死锁问题,造成死锁的大部分原因都是加锁的顺序问题,所以要么避免多次加锁,要么确定加锁的顺序,同时要小心动态锁和相互引用造成的加锁顺序问题。

同时还学习了一些不是加锁出现的问题,比如由于线程优先级太低一直不执行、线程池相互等待、协作线程相互等待等。了解这些情况的原理在开发中才能够避免。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352