示例代码
如下,我们定义了两个资源,创建了两个线程,每个线程都持有一个资源,并请求一个被另外一个线程所持有的资源,故陷入僵持状态,两个线程发生死锁。
public class DeadLockTest {
public static final Object resource1 = new Object();
public static final Object resource2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":等待");
synchronized (resource2) {
System.out.println(Thread.currentThread().getName() + ":可以执行");
}
}
}, "A").start();
new Thread(() -> {
synchronized (resource2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":等待");
synchronized (resource1) {
System.out.println(Thread.currentThread().getName() + ":可以执行");
}
}
}, "B").start();
}
}
什么是死锁(进程或线程)
多个进程或者线程可以竞争有限数量的资源。两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
死锁产生的必要条件(必须全部满足)
- 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
- 请求与保持条件:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
- 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
- 循环等待:有一组等待进程
{P0, P1,..., Pn}
,P0
等待的资源被P1
占有,P1
等待的资源被P2
占有,…,Pn-1
等待的资源被Pn
占有,Pn
等待的资源被P0
占有。
解决死锁的方法
解决死锁的方法可以从多个角度去分析,一般的情况下,有预防,避免,检测和解除四种。
- 预防 是采用某种策略,限制并发进程对资源的请求,从而使得死锁的必要条件在系统执行的任何时间上都不满足。
- 避免则是系统在分配资源时,根据资源的使用情况提前做出预测,从而避免死锁的发生
- 检测是指系统设有专门的机构,当死锁发生时,该机构能够检测死锁的发生,并精确地确定与死锁有关的进程和资源。
- 解除 是与检测相配套的一种措施,用于将进程从死锁状态下解脱出来。