1.什么是死锁
死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期的阻塞,线程不可能正常终止。
【举个栗子】滑稽老铁和女生去吃饺子。吃饺子需要醋和饺子。
滑稽老哥抄起了酱油瓶,女生抄起了醋瓶。
滑稽老铁:你先把醋瓶给我,我才把酱油瓶给你。
女生:你先把酱油瓶给我,我才能给你醋瓶。
如果两者彼此互不相让,就会导致死锁。
酱油和锁相当于两把锁,这两个人相当于两个线程。
【伪代码】
//线程1
synchronized(lock1) {
sychronized(lock2) {
//do something
}
}
//线程2
synchronized(lock2) {
sychronized(lock1) {
//do something
}
}
如果线程1拿到lock1后,线程2开始执行,拿到lock2,此时又开始执行线程1,但是线程1阻塞。线程2也会出现阻塞。
为了进一步阐述死锁的形成,我们引入“哲学家就餐问题”
- 有个桌子,圈着一圈哲学家,桌子上放着足够的意大利面。每个哲学两两之间,放着一根筷子。
- 每个哲学家只做两件事:思考人生 或者吃面条。思考人生的时候就会放下筷子。吃面条就会拿起左右两边的筷子
- 如果哲学家发现筷子被占用,就会阻塞。
- 假设每个哲学家,如果都同时拿起左手边的筷子,然后尝试拿起右手边的筷子,将会发现右手边的筷子都被占用了。但是由于哲学家互不相让,这是就会造成死锁的状态
2.如果避免死锁
死锁产生的四个必要条件:
- 互斥使用,当一个锁被占用的时候,其他线程无法使用。
- 不可抢占,资源请求者不能强制从资源占有者夺取资源,资源只能由资源占有者主动释放。
- 请求和保持,即当资源请求者在请求其他支援的时候同时保持对原有资源的占有。
- 循环等待,多个线程相互等待,造成死锁情况。
上述四个条件都成立的情况下,才能形成死锁的情况。当然,死锁的情况下如果打破上述任何一个条件,便可破解死锁。
2.1破坏循环等待
常用的一种死锁阻止技术就是锁排序。
仍然是上面的哲学家问题:我们给每根筷子编一个序号,只有在获得大序号的筷子的情况下,才能获取小序号的筷子。
此时左上角的哲学家就会失去筷子,其他的哲学家就会获得两个筷子,使用完,并放下筷子。死锁情况也迎刃而解。
【伪代码】
//线程1和线程2约定好,先获取lock1,才能获取lock2
//线程1
synchronized(lock1){
synchronized(lock2){
//do something
}
}
//线程2
synchronized(lock1){
synchronized(lock2){
//do something
{
}