前言
Java的多线程死锁是一种常见的并发问题。它发生在两个或多个线程相互等待对方释放资源,导致程序陷入僵局。死锁可能会导致应用程序停止响应,严重影响性能和可靠性。通常,死锁的发生是由于线程争夺资源的顺序不当或未能释放资源引起的。要解决死锁问题,开发者需要仔细设计线程同步策略,使用锁的层次结构,并确保及时释放锁资源,以避免潜在的死锁风险。
死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
示例:
代码语言:java复制public class DeadLockDemo {
private static Object lock1 = new Object();//锁1,资源1
private static Object lock2 = new Object();//锁2,资源2
public static void main(String[] args) {
//启动一个线程
new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock1){
System.out.println(Thread.currentThread().getName() "拿到了锁1,资源1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() "等待锁2,资源2");
synchronized (lock2){
System.out.println(Thread.currentThread().getName() "拿到了锁2,资源2");
}
}
}
},"线程1").start();
//产生死锁的线程
// new Thread(new Runnable() {
// @Override
// public void run() {
// synchronized(lock2){
// System.out.println(Thread.currentThread().getName() "拿到了锁2,资源2");
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() "等待锁1,资源1");
// synchronized (lock1){
// System.out.println(Thread.currentThread().getName() "拿到了锁1,资源1");
// }
// }
// }
// },"线程2").start();
}
}
线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);
让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。
破坏死锁
代码语言:java复制 //破坏死锁
new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock1){
System.out.println(Thread.currentThread().getName() "拿到了锁1,资源1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() "等待锁2,资源2");
synchronized (lock2){
System.out.println(Thread.currentThread().getName() "拿到了锁2,资源2");
}
}
}
},"线程2").start();
线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。
最后
本期结束咱们下次再见