目录
一. 死锁是什么
死锁是指两个或两个以上的进程在执行过程中, 由于竞争资源或者由于彼此通信而造成的一种阻塞的现象, 若无外力作用, 它们都将无法推进下去; 此时称系统处于死锁状态或系统产生了死锁, 这些永远在互相等待的进程称为死锁进程; 通俗点说, 死锁就是两个或者多个相互竞争资源的线程, 你等我, 我等你, 你不放我也不放, 这就造成了他们之间的互相等待, 导致了 “永久” 阻塞.
一旦程序出现死锁, 就会导致线程无法继续执行后续的工作, 程序势必会有严重的bug, 而且是死锁非常隐蔽的, 开发阶段, 不经意间, 就会写出死锁代码, 还不容易测试出来, 所以这就需要我们对死锁问题有一定的认识以方便我们以后的调试和修改.
二. 死锁的三个典型情况
1. 一个线程一把锁
一个线程一把锁,但是都是不可重入锁。该线程争对这个锁连续加锁就会陷入死锁。如:
此代码就描述了重复加锁,Java中把此类情况设置为可重入
synchronized public void add(){
synchronized(this){
count++;
}
}
2. 两个线程两把锁
两个线程两把锁,把这两个线程先分别获取一把锁,然后再同时尝试获取对方的锁。
Thread t1=new Thread(()->{
synchronized (locker1){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (locker2){
System.out.println("111111111111111111");
}
}
});
Thread t2=new Thread(()->{
synchronized (locker2){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (locker1){
System.out.println("222222222222222222222");
}
}
});
3. N个线程M把锁
三. 可重入、不可重入性
可重入性
同一个线程针对同一个对象, 连续加锁两次, 是否会有问题; 如果没问题, 就是可重入的, 如果有问题, 就是不可重入的.
不可重入性
同一个线程针对同一个对象, 连续加锁两次, 如果锁不是可重入锁, 就会造成死锁问题.
四. 死锁的四个必要条件
- 互斥使用: 线程1拿到了锁, 线程2就得进入阻塞状态(锁的基本特性).
- 不可抢占: 线程1拿到锁之后, 必须是线程1主动释放, 不可能线程1还没有释放, 线程2强行获取到锁.
- 请求和保持: 线程1拿到锁A后, 再去获取锁B的时候, A这把锁仍然保持, 不会因为要获取锁B就把A释放了.
- 循环等待: 线程1先获取锁A再获取锁B, 线程2先获取锁B再获取锁A, 线程1在获取锁B的时候等待线程2释放B,同时线程2在获取锁A的时候等待线程1释放A.
而在Java代码中, 前三点 synchronized
锁的基本特性, 我们是无法改变的, 循环等待是这四个条件里唯一 一个和代码结构相关的, 是我们可以控制的.
五. 如何破解死锁
因为在死锁中,线程的状态一直保持在阻塞状态,所以破解死锁就要打破线程阻塞的这种状态
我们可以使用join()、sleep()、wait notify解决