📜  Java并发-死锁(1)

📅  最后修改于: 2023-12-03 15:02:04.570000             🧑  作者: Mango

Java并发-死锁

在多线程编程中,死锁是一种常见的问题。当两个或多个线程相互等待对方执行完毕,而导致都无法执行下去的情况,就称为死锁。这种情况可能会导致程序停滞,无法继续执行下去。

产生死锁的条件

死锁的产生需要同时满足以下四个条件:

  1. 互斥:资源不能被共享,一次只能被一个线程占用。
  2. 持有和等待:一个线程同时持有一个资源并等待另一个资源被另一个线程占用。
  3. 非抢占:线程不能强制性的抢占其他线程已持有的资源。
  4. 循环等待:多个线程之间形成一种循环等待的关系。

当这些条件满足时,就有可能导致死锁。

示例代码

以下是一个简单的示例代码,演示了如何产生死锁的情况。

public class DeadlockDemo {
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();

        // 线程1持有lock1,等待lock2
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread 1 running");
                }
            }
        });

        // 线程2持有lock2,等待lock1
        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread 2 running");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

在上述示例中,我们创建了两个对象 lock1lock2,并在两个线程中分别使用 synchronized 同步关键字来获取这两个锁。由于线程1持有了 lock1,并等待 lock2,而线程2持有了 lock2,并等待 lock1,因此这两个线程会发生死锁。

避免死锁的方法

为了避免死锁的发生,可以采取以下几种方法:

  1. 破坏互斥:通过设计,改变资源的使用方式,使得多个线程可以同时访问资源。
  2. 破坏持有和等待:一种方法是先获取所有需要的资源,然后再进行后续的操作;另一种方法是通过超时机制来获取资源,如果在指定的时间内没有获取到,则放弃当前操作。
  3. 破坏非抢占:当一个线程获取到资源后,如果发现后续要使用的资源已被其他线程持有,则立即释放当前的资源并等待其他资源。
  4. 破坏循环等待:按顺序获取资源,或者使用其他算法来避免循环等待的情况。
总结

死锁是一个很棘手的问题,在多线程编程中需要特别注意。遇到死锁情况时,可以通过工具来分析和解决问题。在编写多线程代码时,应该在设计和实现阶段尽量避免死锁的发生。