八月 07, 2019

并发编程之死锁、活锁、饥饿

文章字数 2.6k 阅读约需 2 mins. 阅读次数 12530

死锁

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

死锁产生的四个必要条件

  • 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
  • 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  • 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

如何检测

使用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可能永远等待。

0%