死锁
死锁概念
多个线程互相占用着对方需要的资源:线程A持有R1的锁,并等待R2的锁;线程B持有R2的锁,并等待R1的锁!
public class DeadLock {
private final Object MUTEX_READ = new Object();
private final Object MUTEX_WRITE = new Object();
public void read() {
synchronized (MUTEX_READ) {
System.out.println(Thread.currentThread().getName() + " get READ lock !");
synchronized (MUTEX_WRITE) {
System.out.println(Thread.currentThread().getName() + " get Write lock !");
}
System.out.println(Thread.currentThread().getName() + " release Write lock !");
}
System.out.println(Thread.currentThread().getName() + " release READ lock !");
}
public void write() {
synchronized (MUTEX_WRITE) {
System.out.println(Thread.currentThread().getName() + " get Write lock !");
synchronized (MUTEX_READ) {
System.out.println(Thread.currentThread().getName() + " get READ lock !");
}
System.out.println(Thread.currentThread().getName() + " release READ lock !");
}
System.out.println(Thread.currentThread().getName() + " release Write lock !");
}
public static void main(String[] args) {
final DeadLock deadLock = new DeadLock();
new Thread(
() -> {
while (true) {
deadLock.read();
}
}, "READ-THREAD"
).start();
new Thread(
() -> {
while (true) {
deadLock.write();
}
}, "WRITE-THREAD"
).start();
}
}
输出结果:
READ-THREAD get READ lock !
WRITE-THREAD get Write lock !
// ... 程序无法退出
JDK中HashMap不是线程安全的类,如果在多线程同时写操作的情况下不对其进行同步化封装,则很容易出现死循环引起的死锁!
HashMap
不具备线程安全的能力,如果想要使用线程安全的map结构请使用ConcurrentHashMap
或者使用Collections.synchronizedMap
来代替。
死锁需要满足的条件
死锁的四个必要条件:
互斥:进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
请求和保持:进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。(要想破坏请求和保持条件,就要一次性拿到所有资源。)
不可剥夺:进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。(破坏不可剥夺条件:为了解决内置锁的执着,Java 显示锁
支持通知(notify/notifyall
)和等待(wait
))——不可重入锁会出现死锁情况!
环路等待:在发生死锁时,必然存在一个进程—资源的环形链,即进程集合{P1,P2,···,Pn}中的 P1 正在等待一个 P2 占用的资源;P2 正在等待 P3 占用的资源,……,Pn 正在等待已被 P0 占用的资源。(破坏环路等待——只需要将资源序号大小排序获取)