关于java:什么是死锁以及如何解决死锁

2次阅读

共计 2091 个字符,预计需要花费 6 分钟才能阅读完成。

死锁是指两个或多个线程在相互期待对方开释锁的状态,从而导致程序无奈继续执行的状况。

在 Java 多线程中,死锁通常是因为以下四种状况的组合所导致的:

  1. 互斥:多个线程竞争同一资源(如锁),每次只能有一个线程占用,其余线程必须期待。
  2. 占有且期待:线程在持有锁的同时,期待其余线程持有的锁。
  3. 不可抢占:曾经获取锁的线程不能被其余线程强制中断或开释锁。
  4. 循环期待:多个线程造成一个循环期待的关系,每个线程都在期待其余线程开释锁。

要解决死锁问题,能够采取以下措施:

  1. 防止应用多个锁:尽量应用单个锁或者应用更高级别的同步机制,比方并发汇合。
  2. 对立获取锁的程序:确保所有线程获取锁的程序统一,避免出现循环期待的状况。
  3. 尽可能放大同步代码块:缩小同步代码块的长度,放大互斥的范畴。
  4. 及时开释锁:尽可能早地开释锁,防止占有且期待的状况。
  5. 应用定时锁:在获取锁时,应用带有超时工夫的锁,防止因为期待锁而导致死锁。
  6. 强制中断线程:在发现死锁时,能够强制中断其中一个线程,突破循环期待的环。

须要留神的是,死锁是一个简单的问题,解决起来也比拟艰难,须要仔细分析代码和调试。

死锁代码演示:

public class DeadLock {public static void main(String[] args) {Object lockA = new Object();
        Object lockB = new Object();
 
        Thread t1 = new Thread(() -> {
            // 1. 占有一把锁
            synchronized (lockA) {System.out.println("线程 1 取得锁 A");
                // 休眠 1s(让线程 2 有工夫先占有锁 B)try {Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 获取线程 2 的锁 B
                synchronized (lockB) {System.out.println("线程 1 取得锁 B");
                }
            }
        });
        t1.start();
 
        Thread t2 = new Thread(() -> {
            // 占 B 锁
            synchronized (lockB) {System.out.println("线程 2 取得锁 B");
                try {Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 获取线程 1 的锁 A
                synchronized (lockA) {System.out.println("线程 2 取得了锁 A");
                }
            }
        });
        t2.start();}
}

运行后果:

这段代码是一个典型的死锁示例,它蕴含两个线程,每个线程都持有一个锁并期待另一个锁的开释,从而导致死锁。

具体来说,这里定义了两个对象锁 lockA 和 lockB,并在两个线程中别离获取这两个锁。当线程 1 获取锁 A 后,它会休眠 1 秒钟,而后尝试获取锁 B;而当线程 2 获取锁 A 后,它也会休眠 1 秒钟,而后尝试获取锁 B。因为两个线程都在期待对方开释锁,因而它们会始终阻塞在获取锁的代码块中,无奈继续执行,从而造成了死锁。

要解决这个问题,能够依照防止死锁的几种办法之一,例如对锁的获取程序进行对立、尽量放大同步代码块的范畴、及时开释锁等。上面是一种批改计划:

public class UnDeadLock2 {public static void main(String[] args) {Object lockA = new Object();
        Object lockB = new Object();
 
        Thread t1 = new Thread(() -> {synchronized (lockA) {System.out.println("线程 1 失去锁 A");
                try {TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized (lockB) {System.out.println("线程 1 失去锁 B");
                    System.out.println("线程 1 开释锁 B");
                }
                System.out.println("线程 1 开释锁 A");
            }
        }, "线程 1");
        t1.start();
 
        Thread t2 = new Thread(() -> {synchronized (lockA) {System.out.println("线程 2 失去锁 A");
                try {TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized (lockB) {System.out.println("线程 2 失去锁 B");
                    System.out.println("线程 2 开释锁 B");
                }
                System.out.println("线程 2 开释锁 A");
            }
        }, "线程 2");
        t2.start();}
}

在批改后的代码中,两个线程都依照雷同的程序获取锁(先获取锁 A,再获取锁 B),从而防止了死锁问题。此外,线程 2 也及时开释了锁 A,防止了占有且期待的状况。

运行后果:

正文完
 0