死锁活锁和饥饿
死锁
死锁是说两个或多个线程等待着一个线程释放资源而无法执行,而等待的线程又不释放资源导致线程阻塞。使得任务无法执行
发生死锁的必要条件(四个必要条件同时满足才会导致死锁):
互斥(Mutual Exclusion):某些资源只能由一个线程独占使用。
持有并等待(Hold and Wait):线程持有某些资源的同时,还在等待其他资源。
不可剥夺(No Preemption):已分配的资源不能被强制剥夺,必须由持有者主动释放。
循环等待(Circular Wait):存在一个线程集合,其中每个线程都在等待另一个线程持有的资源,形成一个闭环。
1 | class Lock { |
第一个锁锁了自己,想要访问第二个锁,但是第二个锁锁了自己,无法被访问到,同理对于第二个锁也无法访问第一个锁。
解决办法shi
避免循环等待:规定资源获取的顺序,所有线程按照相同的顺序获取资源。使得线程1始终先于线程二执行(同时锁第一个,
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
27class Lock {
static final Object resource1 = new Object();
static final Object resource2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Locked resource1");
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
synchronized (resource2) {
System.out.println("Thread 1: Locked resource2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (resource1) { // 这里改成先锁 resource1,确保顺序一致
System.out.println("Thread 2: Locked resource1");
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
synchronized (resource2) {
System.out.println("Thread 2: Locked resource2");
}
}
});
t1.start();
t2.start();
}
}使用超时机制:如
tryLock()方式获取锁,超时则放弃并重试。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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66import java.util.concurrent.locks.ReentrantLock;
class TryLockSolution {
static final ReentrantLock lock1 = new ReentrantLock();
static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
if (lock1.tryLock()) {
try {
System.out.println("Thread 1: Locked lock1");
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {}
if (lock2.tryLock()) {
try {
System.out.println("Thread 1: Locked lock2");
break;
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {}
}
});
Thread t2 = new Thread(() -> {
while (true) {
if (lock2.tryLock()) {
try {
System.out.println("Thread 2: Locked lock2");
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {}
if (lock1.tryLock()) {
try {
System.out.println("Thread 2: Locked lock1");
break;
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException ignored) {}
}
});
t1.start();
t2.start();
}
}使用Lock + Condition机制:当你希望先获取到两个资源再执行任务时候,可以使用这个方法
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
30import java.util.concurrent.locks.ReentrantLock;
class LockConditionSolution {
static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 1: Acquired lock, performing operations...");
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
} finally {
lock.unlock();
}
});
Thread t2 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 2: Acquired lock, performing operations...");
try { Thread.sleep(100); } catch (InterruptedException ignored) {}
} finally {
lock.unlock();
}
});
t1.start();
t2.start();
}
}lock.lock()确保只有一个线程能进入临界区,所以不会有两个线程同时获取不同的锁,从而避免了死锁- 使用Timeout机制
获取锁失败就释放已有的锁并退出,避免无限等待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
48
49
50
51
52
53
54
55
56
57
58
59
60import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
class TimeoutSolution {
static final ReentrantLock lock1 = new ReentrantLock();
static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
if (lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Locked lock1");
Thread.sleep(100);
if (lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Locked lock2");
} finally {
lock2.unlock();
}
} else {
System.out.println("Thread 1: Couldn't lock lock2, releasing lock1");
}
} finally {
lock1.unlock();
}
} else {
System.out.println("Thread 1: Couldn't lock lock1");
}
} catch (InterruptedException ignored) {}
});
Thread t2 = new Thread(() -> {
try {
if (lock2.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Locked lock2");
Thread.sleep(100);
if (lock1.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Locked lock1");
} finally {
lock1.unlock();
}
} else {
System.out.println("Thread 2: Couldn't lock lock1, releasing lock2");
}
} finally {
lock2.unlock();
}
} else {
System.out.println("Thread 2: Couldn't lock lock2");
}
} catch (InterruptedException ignored) {}
});
t1.start();
t2.start();
}
}
- 使用Timeout机制
活锁
两个线程间相互让步导致没能完成任务,这种情况下线程并没有阻塞,但是一直在做没有用的循环导致没有成功
1 | import java.util.concurrent.locks.ReentrantLock; |
线程一获得锁1后尝试获得锁二来执行任务,线程二获得锁二后尝试获得线程一来执行任务,但是线程一无法拿到线程二的锁,于是释放资源后重新开始,线程二也在做一样的事,两个线程相互让导致活锁死循环。
解决方案:
引入随机等待时间,避免两个线程总是同时尝试相同的操作。
使用更高级的锁机制,如
ReentrantLock的tryLock(time, unit),超时后放弃尝试
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 梦始!
评论
