java并发 死锁

参考链接:

http://tutorials.jenkov.com/java-concurrency/deadlock.html

http://tutorials.jenkov.com/java-concurrency/deadlock-prevention.html

学习小结

线程死锁

并发系统中经常需要对资源加锁,如果多个线程在同一时间对相同资源加锁时可能会发生死锁。但是如果多个线程在同一时间对多个相同资源加锁,且加锁顺序不同,则必定会死锁。

比如两个资源A和B,两个线程T1和T2。现在T1已经加锁A,欲对B加锁,而T2已对B加锁,欲对A加锁。现在AB都被加锁了,其他线程都不能获得锁,两个线程相互需要对方的锁,于是都被阻塞,产生死锁。

这是一个简单的情况,复杂的是多个线程之间产生死锁。

我的理解就是,死锁的一个形象化的描述是,将需要这种意愿表示箭头符号,不同线程中含有不同资源(锁),被箭头连接后会有环路,这就是死锁的图示。比如下图:

不同线程含有不同资源,该资源不被其他线程使用,每个线程都需要其他线程的资源,用箭头标注。获得不了,于是全部阻塞,死锁产生,从图中可以看出,这是个环路。

数据库死锁

对于我来说,数据库方面的锁接触较少,所以读到这里,感觉还是有点收获的。

数据库中的死锁经常发生在事务中。每个事务也许含有多条更新操作的sql语句,每条被该事务修改过的记录都会被该事务加锁,直到事务完成才释放锁。这与线程死锁是类似的,当该事务需要更新的记录被其他线程加锁,而其他线程需要的资源也被占用,最终产生如上说的环路,最终死锁产生。

死锁预防

1、加锁顺序

当不同线程需要相同锁,但是以不同的顺序获得时,可能会发生死锁。如果让每个线程加锁的顺序一致,不就解决死锁了?是的,这是最简单的方法,但是需要你能够了解所有线程加锁的顺序。但却并不总是这种情况,很大可能,你并不清楚所有线程加锁的顺序。

2、加锁超时

通过设置一个时长,当线程想要获得所有需要的锁时,发现超时了,那么很有可能发生死锁了,因此回退到没有加锁的地方。等待一个随机的时间,再次尝试获得所有的锁。在这等待时间内,也许其他线程因为没有死锁的烦恼,成功获得所得所需的锁。

但是这是有一定的问题的,比如线程多的时候,即使你回退到原点,当再次尝试的时候,发现依旧超时,又回退,反反复复。这是因为线程多了,回退的线程之间等待间隔时间很短,还是会产生冲突,导致死锁的。

超时也不一定意味着发送了死锁,可能是由于在等待某个锁的时间过长,导致超时的。

更重要的是,java的synchronized同步块不能设置一个超时时间。需要自定义一个锁,或者使用 java.util.concurrency 包中类。

3、死锁检测

所有线程在获得一个锁时,就将该信息记录在某种数据结构中,比如图。当某个线程被阻塞时就遍历该数据结构,看锁的需求链能够构成环路。如果有环路就证明有死锁产生了。

那怎么解决死锁呢?

一种就是该线程回退到原点,释放所有锁。但是如果请求相同资源的线程太多了,他们会反复处于死锁状态,即使他们已经回退过了。

一个好的优化是为不同的线程设置优先级。低优先级的将回退,高优先级的将获得锁。这让我想到了操作系统的调度问题。

和加锁超时不同,死锁检测到了就表明是真的产生了死锁。

猜你喜欢

转载自blog.csdn.net/jdbdh/article/details/81877775
今日推荐