什么是死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线
程被无限期地阻塞,因此程序不可能正常终止
死锁的4个必要条件
互斥使用: 当一个资源被一个线程占用时,另一个线程不可以使用
不可抢占: 资源请求者不可以从资源持有者手中去抢夺资源,只可以等待资源持有者来进行主动释放
请求和保持: 资源请求者在请求一个资源时,同时保持对一个资源的占有
循环等待: P1占有P2所需要的资源,P2占有P3所需要的资源,P3占有P1所需要的资源,形成了一个等待环路
所以如果我们想要避免死锁就只需要打破上述4个必要条件中的一种。就可以来有效避免。其中条件“循环等待”就是我们最好下手的条件(破坏循环等待)
哲学家就餐问题
每个 哲学家 只做两件事: 思考人生 或者 吃面条. 思考人生的时候就会放下筷子. 吃面条就会拿起左
右两边的筷子(先拿起左边, 再拿起右边)
当4号哲学家正在吃面时,哲学家3号就会等带哲学家4号(进入阻塞等待),等到哲学家4号吃完,放下筷子后就会拿起筷子吃面
一般情况下会正常进行,但假设同一时刻, 五个 哲学家 同时拿起左手边的筷子, 然后再尝试拿右手的筷子, 就会发现右手的筷子都被占用了. 由于 哲学家 们互不相让, 这个时候就形成了死锁
解决方法如下
代码演示如下:
当没有进行约定时
public static void main(String[] args) {
Object locker1 = new Object();
Object locker2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (locker1){
synchronized (locker2){
//do something
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
synchronized (locker2){
synchronized (locker1){
//do something
}
}
});
t2.start();
}
约定好先获取 lock1, 再获取 lock2 , 就不会环路等待
public static void main(String[] args) {
Object locker1 = new Object();
Object locker2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (locker1){
synchronized (locker2){
//do something
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
synchronized (locker1){
synchronized (locker2){
//do something
}
}
});
t2.start();
}