1.死锁是如何产生
多个线程同时被阻塞
它们其中的一个或者多个等待某个资源的释放
而由于线程全部阻塞,资源的释放无限期的延迟
因此程序不可能终止
2.死锁的三个典型情况
2.1 一个线程一把锁,可重入锁没事,不可重入锁死锁
2.2 两个线程两把锁,即使是可重入锁,也会出现死锁
public class ThreadDemo {
static class Add{
public int count;
public void add(int count) {
count++;
}
}
public static void main(String[] args) {
Object blocker1 = new Object();
Object blocker2 = new Object();
Add a= new Add();
// blocker2 释放后 blocker1 才解锁
Thread t1 = new Thread(() -> {
synchronized (blocker1) {
synchronized (blocker2) {
for (int i = 0; i < 5000; i++) {
a.add(a.count);
}
System.out.println(a.count);
}
}
});
// blocker1 释放后 blocker2 才解锁
Thread t2 = new Thread(() -> {
synchronized(blocker2) {
synchronized (blocker1) {
for (int i = 0; i < 5000; i++) {
a.add(a.count);
}
System.out.println(a.count);
}
}
});
}
}
循环等待
2.3 n 个线程, m 把锁(哲学家就餐问题)
每个哲学家两两之间放着一根筷子
每个哲学家有么思考人生,要么拿起两根筷子吃面(先拿左再拿右)
假如哲学家同时拿起左手边筷子,如果再拿起右手边就会发现筷子都被占用了
哲学家互不相让就会出现死锁
3.死锁的四个必要条件
3.1 互斥使用
当资源被一个线程使用时,另外的线程不能使用
3.2 不可抢占
一个线程拿到锁只能自己主动释放锁,其他的线程不可强行占有
3.3 请求和保持
当资源请求者保持对原有的资源占用,同时在请求其他的资源(吃着着碗里的,看着锅里的)
3.4 循环等待
存在一个等待队列, q1占用q2, q2占用q1(家里钥匙落车里,车钥匙落家里)
4.如何打破死锁
任意打破死锁的四个必要条件之一
而循环等待是最容易打破的,我们可以把每个锁进行排序
锁排序
n个线程m个锁,针对m个锁从小到大进行锁排序
n个线程获取锁的时候,按照编号从小到大进行获取锁
这样就可以避免循环等待
这样哲学家5,想要4号筷子就得先释放5号筷子(锁) 避免死锁