死锁
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。
死锁产生的四个必要条件
- 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
- 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞
如何检测
使用jstack或者jconsole可查看线程堆栈。
"Thread-1" prio=10 tid=0x00007fe8c00a0800 nid=0x17cc waiting for monitor entry [0x00007fe8c5031000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLocakTest$DeadThread2.run(DeadLocakTest.java:61)
- waiting to lock <0x00000000af24b248> (a java.lang.Object)
- locked <0x00000000af24b258> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"Thread-0" prio=10 tid=0x00007fe8c009e800 nid=0x17cb waiting for monitor entry [0x00007fe8c5132000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLocakTest$DeadThread1.run(DeadLocakTest.java:38)
- waiting to lock <0x00000000af24b258> (a java.lang.Object)
- locked <0x00000000af24b248> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
Thread-1
locked <0x00000000af24b258>
waiting to lock 0x00000000af24b248
Thread-0
locked 0x00000000af24b248
waiting to lock 0x00000000af24b258
如何解决
- 破坏请求与保持条件:一次性申请所有的资源。
- 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件:按序申请资源,反序释放资源。
活锁
活锁指的是 任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
为解决活锁可以引入一些随机性,例如如果检测到冲突,那么就暂停随机的一定时间进行重试。这回大大减少碰撞的可能性。典型的例子是以太网的CSMA/CD检测机制。
饥饿
饥饿:是指如果线程T1占用了资源R,线程T2又请求封锁R,于是T2等待。T3也请求资源R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T4的请求…,T2可能永远等待。