死锁以及如何避免死锁

死锁

锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。

例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。

该情况如下:

Thread 1 locks A, waits for B
Thread 2 locks B, waits for A

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package cn.caoler.DeadLockDemo;

/**
* Author: Caole
* CreateDateTime: 2017/11/23 11:56
* Description:
*/
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";

public static void main(String[] args) {
new DeadLockDemo().deadLock();
}

private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized(A){
try {
Thread.currentThread().sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(B){
System.out.println("1");
}
}
}
});

Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized(B){
synchronized(A){
System.out.println("2");
}
}
}
});

t1.start();
t2.start();
}
}

避免死锁

常见方法有如下几个:

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在同一个数据库连接里,否则会出现解锁异常的情况。

具体解释详见并发编程网-避免死锁

0%