避免死锁主要有 3种方式
加锁顺序
加锁时限
死锁检测
加锁顺序
一个线程如果要获取多个锁,必须按照一定的顺序去获取,比如要获取A B C 三把锁,我们规定,必须获取到了 AB 两把锁,才能去获取 C锁
举个例子 现在有两个线程 都要去获取 ABC 三把锁
然后 1号 线程 获取到了 A 和 B 两把锁
2号 线程获取了 C锁
于是 1号线程在等待 2号线程释放锁C锁
2号线程在等待 1 号线程释放 A 和 B 锁
于是出现了死锁
现在采用加锁队列后,必须先 获取 A 和 B 才能获取C锁,所以 2号线程就没有机会在没有获取 AB 的情况下拿到C锁,就不会出现死锁问题
这个策略的最大问题是,你必须提前知道所有用到的锁,这个有些时候难以预测
加锁时限
一个线程尝试去获取锁,如果在指定的时间内获取不到,就放弃等待锁,并释放自己现在所持有的锁,然后随机等待一定时间,再去获取锁
这里要注意的是 ,等待的时间一定要是随机,不然可能出现 两个线程都释放资源,然后等待相同时间,然后再一起去获取锁,于是又死锁,这种现象称为活锁
死锁检测
只有检测到死锁的状态,才能做针对性的业务处理。
ThreadMXBean是java.lang.management包提供的工具类,通过ThreadMXBean类的findDeadlockedThreads()方法能获得发生死锁的线程编号。
public class DeadLockDemo {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void doActionA() {
synchronized (lock1) {
System.out.println(Thread.currentThread() + ": acquire lock1");
try {
// 线程阻塞,让其他线程拿到lock2
Thread.sleep(10);
} catch (Exception e) {
}
synchronized (lock2) {
System.out.println(Thread.currentThread() + ": acquire lock2");
}
}
}
public void doActionB() {
synchronized (lock2) {
System.out.println(Thread.currentThread() + ": acquire lock2");
synchronized (lock1) {
System.out.println(Thread.currentThread() + ": acquire lock1");
}
}
}
public static void main(String[] args) {
Thread monitorThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
long[] ids = tmx.findDeadlockedThreads();
if (ids != null) {
ThreadInfo[] tmxThreadInfo = tmx.getThreadInfo(ids, true, true);
System.out.println("The following threads are deadlocked:");
for (ThreadInfo ti : tmxThreadInfo) {
System.out.println(ti);
Thread thread = findThread(ti.getThreadId());
thread.interrupt();
}
break;
}
}
}
});
monitorThread.start();
DeadLockDemo demo = new DeadLockDemo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
demo.doActionA();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
demo.doActionB();
}
});
thread1.start();
thread2.start();
}
public static Thread findThread(long threadId) {
ThreadGroup group = Thread.currentThread().getThreadGroup();
while(group != null) {
Thread[] threads = new Thread[(int)(group.activeCount() * 1.2)];
int count = group.enumerate(threads, true);
for(int i = 0; i < count; i++) {
if(threadId == threads[i].getId()) {
return threads[i];
}
}
group = group.getParent();
}
return null;
}
}