所谓死锁,就是两个线程互相等待对方释放锁,即A持有B要获取的锁,B持有A要获取的锁,然后B要获取A持有的锁,A要获取B持有的锁。
所以死锁至少有两把锁,对于只有一把锁的情况,不会出现死锁,只是阻塞Block状态,当持有线程释放掉锁的之后,其他线程就可以继续获取锁。
模拟下死锁,示例代码:
public class DeadLock {
static Object o1 = new Object();
static Object o2 = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
lock1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
lock2();
}
}).start();
}
public static void lock1(){
synchronized (o1){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("o2执行完毕");
}
System.out.println("o1执行完毕");
}
}
public static void lock2(){
synchronized (o2){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("o2执行完毕");
}
}
}
程序执行之后,栈内信息如下:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001c0af638 (object 0x000000076b742460, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001c0acda8 (object 0x000000076b742470, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.test.DeadLock.lock2(DeadLock.java:56)
- waiting to lock <0x000000076b742460> (a java.lang.Object)
- locked <0x000000076b742470> (a java.lang.Object)
at com.test.DeadLock$2.run(DeadLock.java:22)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.test.DeadLock.lock1(DeadLock.java:37)
- waiting to lock <0x000000076b742470> (a java.lang.Object)
- locked <0x000000076b742460> (a java.lang.Object)
at com.test.DeadLock$1.run(DeadLock.java:15)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
在我们实际开发中,死锁的情况不是很多,但是之前的确处理过死锁的案例。我遇到的案例大概是这样的
表A和表B没有太大关联。单看某个业务方法,并没有啥问题,但是当实际运行的时候,大部分时间也不会遇到问题,但是一旦出现有一个业务线程调用了业务方法1并且执行了删除表A数据D1,这个时候另一个线程的业务调用了业务方法2并且执行链路删除数据B数据D2, 此时第一个线程要执行删除表B数据D2,第二个线程要执行删除表A数据D1的时候,两个线程造成了死锁。这个是行锁,如果是表锁会更加容易复现。我们也可以通过数据库的两个窗口进行模拟这种死锁。
所以我们在做业务的时候,尽量避免这种情况。避免这种情况也很简单,要么就把删除A和删除B的业务单独抽离出来,所有需要删除A或删除B的都调我统一的服务来执行。要么两个业务执行的顺序要一致,先删A再删B或者先删B再删A,一致的话也不会造成死锁。