下面是Java中常见的死锁以及解决方法的完整攻略。
什么是死锁?
死锁是指在并发编程中,两个或多个线程互相持有对方需要的资源,从而造成它们都无法继续执行的情况。此时,程序会进入一个死循环状态,无法正常运行,也无法进行下一步操作。
常见的死锁场景
以下是一些常见的导致死锁的场景:
1. 多个线程竞争同一资源
多个线程同时竞争同一个资源,如果每个线程都持有该资源的一部分,就有可能会造成死锁。
示例代码
public class DeadLockDemo implements Runnable {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
@Override
public void run() {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
}
}
}
public static void main(String[] args) {
DeadLockDemo demo = new DeadLockDemo();
Thread t1 = new Thread(demo, "Thread1");
Thread t2 = new Thread(demo, "Thread2");
t1.start();
t2.start();
}
}
上述示例中,线程1获取了lock1的锁,然后休眠一秒钟,之后尝试获取lock2的锁;而线程2则相反,它先获取lock2的锁,然后尝试获取lock1的锁。这样就容易导致死锁。
2. 循环等待
每个线程都在等待其他线程释放它所需要的资源,就会造成循环等待的情况,最终导致死锁。
示例代码
public class DeadLockDemo2 {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
}
}
}, "Thread1");
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
}
}
}, "Thread2");
t1.start();
t2.start();
}
}
上述示例中,线程1获取了lock1的锁,休眠一秒钟之后,尝试获取lock2的锁;而线程2则相反,它先获取lock2的锁,然后尝试获取lock1的锁。这样就容易导致死锁。
解决死锁的方法
针对上述出现死锁的情况,有以下几种常见解决方法:
1. 避免嵌套加锁
尽可能避免嵌套加锁,如果必须要嵌套加锁,那么应该尽可能保证所有线程都按照相同的顺序获取锁,即先获取锁1,再获取锁2。
示例代码
public class NoDeadLockDemo implements Runnable {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
private final boolean flag;
public NoDeadLockDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
}
}
} else {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
}
}
}
}
public static void main(String[] args) {
NoDeadLockDemo demo1 = new NoDeadLockDemo(true);
NoDeadLockDemo demo2 = new NoDeadLockDemo(false);
Thread t1 = new Thread(demo1, "Thread1");
Thread t2 = new Thread(demo2, "Thread2");
t1.start();
t2.start();
}
}
上述示例中,我们把锁分成了两个,每个线程只获取一个锁。如果需要获取两个锁,按照相同的顺序来获取锁,就不会出现死锁。
2. 使用锁的超时机制
使用 tryLock()
方法获取锁,设置超时时间。当在指定的时间段内没有获取到所需要的锁时,立即释放已经获得的锁。
示例代码
public class TimeOutDeadLockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
if (lock1.equals(lock2)) {
System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
} else {
while (true) {
boolean b1 = false, b2 = false;
try {
b1 = acquire(lock1, 100);
b2 = acquire(lock2, 100);
if (b1 && b2) {
System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (b1) {
release(lock1);
}
if (b2) {
release(lock2);
}
}
}
}
} finally {
System.out.println(Thread.currentThread().getName() + " 退出");
}
}, "Thread1");
Thread t2 = new Thread(() -> {
try {
if (lock2.equals(lock1)) {
System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
} else {
while (true) {
boolean b1 = false, b2 = false;
try {
b1 = acquire(lock2, 200);
b2 = acquire(lock1, 200);
if (b1 && b2) {
System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (b1) {
release(lock2);
}
if (b2) {
release(lock1);
}
}
}
}
} finally {
System.out.println(Thread.currentThread().getName() + " 退出");
}
}, "Thread2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("主线程退出");
}
public static boolean acquire(Object lock, long timeout) throws InterruptedException {
long start = System.currentTimeMillis();
long end = start + timeout;
while (System.currentTimeMillis() < end) {
if (Thread.holdsLock(lock)) {
return true;
}
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
if (lock instanceof ReentrantLock) {
ReentrantLock reentrantLock = (ReentrantLock) lock;
if (reentrantLock.tryLock(timeout - (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS)) {
return true;
}
} else {
synchronized (lock) {
if (Thread.holdsLock(lock)) {
return true;
}
lock.wait(timeout - (System.currentTimeMillis() - start));
}
}
}
return false;
}
public static void release(Object lock) {
if (lock instanceof ReentrantLock) {
ReentrantLock reentrantLock = (ReentrantLock) lock;
if (reentrantLock.isHeldByCurrentThread()) {
reentrantLock.unlock();
}
} else {
synchronized (lock) {
lock.notifyAll();
}
}
}
}
上述示例中,我们使用了 tryLock()
方法获取锁,并设置了超时时间。如果在指定时间内未能获取到锁,就会立即释放已经获取的锁,避免死锁。同时,我们定义了 acquire()
和 release()
两个方法来封装获取锁和释放锁的操作,可以根据实际情况来调用不同的方法。
总结
死锁是多线程编程中很容易出现的问题,我们必须采取一些措施来避免死锁的发生。一般来说,可以通过避免嵌套锁以及使用锁的超时机制等方式来解决死锁问题。但在实际开发中,还需要根据具体情况来选择最合适的方案。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中常见的死锁以及解决方法代码 - Python技术站