死锁是指在多线程程序中,两个或多个线程互相等待对方释放锁资源,从而导致所有线程都无法继续执行的一种情况。当发生死锁时,程序会一直等待,直到超时或者强制终止,导致程序异常或崩溃。Java中的死锁是一种非常常见的并发问题,解决死锁问题是多线程编程中不可避免的挑战之一。
具体例子
死锁的产生原因通常是多个线程在争夺同一组锁资源,而且每个线程都需要获取其他线程持有的锁资源才能继续执行。如果线程A持有锁资源1,想要获取锁资源2,而线程B持有锁资源2,想要获取锁资源1,那么就会发生死锁。以下是一个简单的死锁示例:
public class DeadlockDemo {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " acquired lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " acquired lock2");
}
}
}, "Thread1").start();
new Thread(() -> {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " acquired lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " acquired lock1");
}
}
}, "Thread2").start();
}
}
在这个例子中,我们创建了两个线程,分别尝试获取lock1和lock2两个锁资源。Thread1先获取了lock1,然后尝试获取lock2;Thread2先获取了lock2,然后尝试获取lock1。由于Thread1和Thread2互相等待对方释放锁资源,最终导致程序进入死锁状态。
怎样避免死锁
避免死锁的方法通常有以下几种:
- 避免嵌套锁:如果一个线程持有了一个锁,就不要再去尝试获取其他锁资源,否则容易导致死锁。如果确实需要获取其他锁资源,可以尝试释放已经获取的锁资源再去获取其他锁资源。
- 避免循环依赖:如果多个线程需要获取多个锁资源,就要尽量避免循环依赖的情况。如果必须要依赖其他线程持有的锁资源,就要按照固定的顺序获取锁资源,以避免死锁。
- 使用定时锁:如果一个线程持有了一个锁,但无法获取其他锁资源,可以尝试使用定时锁来避免死锁。如果等待时间超过了指定的时间,就放弃获取锁资源,从而避免死锁。
以下是一个使用定时锁避免死锁的例子:
public class AvoidDeadlockDemo {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " acquired lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tryLock(lock2)) {
System.out.println(Thread.currentThread().getName() + " acquired lock2");
} else {
System.out.println(Thread.currentThread().getName() + " failed to acquire lock2");
unlock(lock1);
}
}
}, "Thread1").start();
new Thread(() -> {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " acquired lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tryLock(lock1)) {
System.out.println(Thread.currentThread().getName() + " acquired lock1");
} else {
System.out.println(Thread.currentThread().getName() + " failed to acquire lock1");
unlock(lock2);
}
}
}, "Thread2").start();
}
private static boolean tryLock(Object lock) {
long start = System.currentTimeMillis();
long timeout = 5000;
while (true) {
synchronized (lock) {
if (lock == lock1 && System.currentTimeMillis() - start > timeout) {
return false;
} else if (lock == lock2 && System.currentTimeMillis() - start > timeout) {
return false;
} else if (lock == lock1 && Thread.holdsLock(lock2)) {
continue;
} else if (lock == lock2 && Thread.holdsLock(lock1)) {
continue;
} else {
return true;
}
}
}
}
private static void unlock(Object lock) {
synchronized (lock) {
lock.notifyAll();
}
}
}
在这个例子中,我们创建了两个线程,分别尝试获取lock1和lock2两个锁资源。如果一个线程无法获取锁资源,就会尝试等待5秒钟,然后放弃获取锁资源,从而避免死锁。如果一个线程成功获取了锁资源,就可以执行相应的操作,然后释放锁资源,让其他线程可以继续执行。
总之,避免死锁是多线程编程中非常重要的一个问题,需要仔细分析程序逻辑,合理设计锁资源的获取和释放顺序,以确保程序的正确性和健壮性。