死锁(Deadlock)是多线程编程中的一个经典问题,指的是两个或多个线程互相等待对方释放资源,从而导致程序无法继续执行的状态。
死锁产生的条件,通常被称为死锁的“必要条件”,包括:
-
互斥条件(Mutual Exclusion): 一个资源同时只能被一个线程占用。
-
请求与保持条件(Hold and Wait): 一个线程在持有一个资源的同时,又请求另一个资源。
-
不剥夺条件(No Preemption): 资源只能由持有它的线程释放,不能被强制性地剥夺。
-
循环等待条件(Circular Wait): 若干线程之间形成一种头尾相接的循环等待资源的关系。
死锁代码:
java">public class DeadLock {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
Thread thread1 = new Thread(A);
thread1.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
Thread thread2 = new Thread(B);
thread2.setName("B 线程");
thread1.start();
thread2.start();
}
}
class DeadLockDemo implements Runnable{
static final Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static final Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + " 进入 1");
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入 3");
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 4");
}
}
}
}
}
为了避免死锁,可以采取以下策略:
-
破坏互斥条件: 在某些情况下,可以让多个线程共享某些资源,避免互斥。
-
破坏请求与保持条件: 要求一个线程在请求资源时,释放已持有的资源。
-
破坏不剥夺条件: 允许操作系统剥夺某些线程的资源。
-
破坏循环等待条件: 对资源进行排序,按照一定的顺序申请资源,避免形成环状等待。
释放锁的操作一般发生在线程使用完共享资源后,应当确保及时释放资源以允许其他线程使用。Java 中,释放锁通常在以下4种情况下发生:
- 当线程执行完共享资源的操作后,应该显式地调用释放锁的方法,例如使用 synchronized关键字时,在代码块执行完毕后会自动释放锁
- 当前线程在同步代码块、同步方法中遇到break或return
- 如果在使用共享资源的过程中抛出异常,为了避免资源一直被占用,应当在异常处理代码中释放锁。
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
下面的操作不会释放锁
- 线程执行同步代码块或同步方法时,程序调用**Thread.sleep()、Thread.yield()**暂停当前线程的执行,不会释放锁
- 线程执行同步代码块时,其他线程调用了该线程的**suspend()**将该线程挂起,该线程不会释放锁。suspend()和resume()已经过时,尽量避免使用
确保正确地释放锁对于避免死锁和保证多线程程序的正确性非常重要。
总之,死锁是多线程编程中需要注意的重要问题,应当谨慎设计和管理多线程程序,避免死锁的产生,并在合适的时机释放锁以确保程序的正常运行。