一、什么是死锁
死锁是指两个或以上的进程在互相请求资源的时候形成了僵局,导致它们等待对方释放资源而无法继续执行下去。在这种情况下,这些进程永远阻塞并且等待资源。死锁是一种非常常见的并发问题,在多线程编程中尤其需要注意。
二、死锁的必要条件
死锁的发生需要满足以下四个必要条件:
- 互斥条件:资源不能被共享,只能由一个进程占用。
- 请求与保持条件:一个进程已经得到了资源,但是还需要请求其他资源。
- 非抢占条件:进程不能强制从别的进程中获取资源,只能通过自己释放资源来让其他进程获取资源。
- 循环等待条件:存在一个循环等待的进程链。
三、死锁代码示例
public class DeadLockDemo { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized(lock1) { System.out.println("Thread1 holds lock1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock2) { System.out.println("Thread1 holds lock1 and lock2"); } } }).start(); new Thread(() -> { synchronized(lock2) { System.out.println("Thread2 holds lock2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock1) { System.out.println("Thread2 holds lock1 and lock2"); } } }).start(); } }
四、死锁解决方法
避免和解决死锁问题有以下几种方法:
- 避免循环等待
- 破坏互斥条件
- 采用超时机制等待资源的释放
- 资源一致性管理
- 使用锁的层次结构来避免死锁
五、使用锁的层次结构避免死锁
public class LockHierarchyDemo { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized(lock1) { System.out.println("Thread1 holds lock1"); synchronized(lock2) { System.out.println("Thread1 holds lock1 and lock2"); } } }); Thread thread2 = new Thread(() -> { synchronized(lock1) { System.out.println("Thread2 holds lock1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock2) { System.out.println("Thread2 holds lock1 and lock2"); } } }); thread1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); } }
这个示例中,两个线程请求锁的顺序一致,不会发生循环依赖的情况。如果一个线程请求了多个锁,并在请求锁的时候等待时间超过了阈值,该线程释放所有的锁,等待一段时间后再次请求锁。这种方式可以减小死锁的概率。