0.什么是死锁
死锁(Deadlock)是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果不提前预防或外界干扰,这些线程将无法执行下去
1.造成死锁的原因
造成死锁的原因有,互斥、请求和保持、不可抢占、循环等待
互斥:一个资源只能被一个进程独占,即该资源只能被一个进程占有或使用。
请求和保持:进程已经保持了至少一个资源,并还在尝试获取其他进程保持的资源。
不可抢占:某个资源只能由占有它的线程释放,不能被其他线程强制占有或抢占。
循环等待:假设一个队列有{t1,t2,t3}这三个线程,t1 等待 t2 所占资源,t2 等待 t3 所占资源,t3 等待 t1 所占资源,这样就会造成循环等待。
2.避免死锁的策略
按照固定的顺序请求资源:确保所有线程都按照相同的顺序来请求资源,这样可以减少死锁的可能性。
避免循环等待:确保线程之间不存在循环等待资源的关系
使用锁超时设置:Java中可以使用tryLock()
方法来设置锁的超时时间,以便在超时后自动释放锁,减少死锁的发生
3.如何构造一个死锁
package org.monkey.deadlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadLockTest {
public static void main(String[] args) {
testDeadLock();
}
private static void testDeadLock() {
Object obj1 = new Object(); //资源1
Object obj2 = new Object(); //资源2
Thread thread1 = new Thread(() -> {
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "获取到了锁 obj1");
// 获得obj1后等待一秒再获取obj2
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + " 获取到了锁 obj2");
}
}
}, "th-1");
Thread thread2 = new Thread(() -> {
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "获取到了锁 obj2");
// 获得obj1后等待一秒再获取obj2
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + " 获取到了锁 obj1");
}
}
}, "th-2");
thread1.start();
thread2.start();
}
}
3.tryLock 解决死锁demo
package org.monkey.deadlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadLockTest {
public static void main(String[] args) {
testDeadLockWithTryLock();
}
private static void testDeadLockWithTryLock() {
ReentrantLock lock1 = new ReentrantLock(); //资源1
ReentrantLock lock2 = new ReentrantLock(); //资源2
long TIMEOUT = 5000L; // 锁的超时时间
Thread thread1 = new Thread(() -> {
try {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "尝试获取 lock1");
if (!lock1.tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
System.out.println(threadName + "获取 lock1 失败");
throw new Exception("Failed to acquire lock1");
}
System.out.println(threadName + "获取到 lock1");
Thread.sleep(1000L);
System.out.println(threadName + "尝试获取 lock2");
if (!lock2.tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
System.out.println(threadName + "获取 lock2 失败");
throw new Exception("Failed to acquire lock2");
}
System.out.println(threadName + "获取到 lock2");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock2.unlock();
lock1.unlock();
}
}, "th-1");
Thread thread2 = new Thread(() -> {
try {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "尝试获取 lock2");
if (!lock1.tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
System.out.println(threadName + "获取 lock2 失败");
throw new Exception("Failed to acquire lock2");
}
System.out.println(threadName + "获取到 lock2");
Thread.sleep(6000L);
System.out.println(threadName + "尝试获取 lock1");
if (!lock2.tryLock(TIMEOUT, TimeUnit.MILLISECONDS)) {
System.out.println(threadName + "获取 lock1 失败");
throw new Exception("Failed to acquire lock1");
}
System.out.println(threadName + "获取到 lock1");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock2.unlock();
lock1.unlock();
}
}, "th-2");
thread1.start();
thread2.start();
}
}