前言

并发的程序及时处理的能力被称之为活跃度。以下将介绍最常见的活跃度的问题——死锁,以及接着短暂地介绍其他两种活跃度问题——饥饿和活锁。

死锁

死锁描述了一个两个或者更多的线程相互地等待资源导致永久堵塞的情况。
案例1:当运行以下的Deadlock的时候,当两个线程都想要去调用 bowBack() 的时候,它们很有可能会发生阻塞。两个线程永远都不会结束,因为每一个线程都在等待另外一个退出bow() 。

  1. public class Deadlock {
  2. static class Friend {
  3. private final String name;
  4. public Friend(String name) {
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return this.name;
  9. }
  10. public synchronized void bow(Friend bower) {
  11. System.out.format("%s: %s"
  12. + " has bowed to me!%n",
  13. this.name, bower.getName());
  14. bower.bowBack(this);
  15. }
  16. public synchronized void bowBack(Friend bower) {
  17. System.out.format("%s: %s"
  18. + " has bowed back to me!%n",
  19. this.name, bower.getName());
  20. }
  21. }
  22. public static void main(String[] args) {
  23. final Friend alphonse =
  24. new Friend("Alphonse");
  25. final Friend gaston =
  26. new Friend("Gaston");
  27. new Thread(new Runnable() {
  28. public void run() { alphonse.bow(gaston); }
  29. }).start();
  30. new Thread(new Runnable() {
  31. public void run() { gaston.bow(alphonse); }
  32. }).start();
  33. }
  34. }

饥饿和活锁

饥饿和活锁比死锁少见很多。但是依然是每一个并发软件开发者可能遭遇到的问题。

饥饿

饥饿描述了一个线程无法获得对共享资源的常规访问并且无法取得进度的情况。这发生在当共享资源被贪婪线程造成长时间不可用的时候。例如:假设一个对象提供的同步方法经常耗费很长时间来return,如果一个线程频繁地调用这个方法,另外同样需要频繁同步访问相同的对象的线程则会经常被阻塞。

活锁

一个线程经常响应另一个线程的操作,如果另外线程的操作同样响应另外线程的操作,结果可能会导致活锁。和死锁一样,活锁线程无法进行进一步的处理。然而,这些线程没有被阻塞,他们只是忙着回复对方,无暇继续工作。这相当于两个在走廊的人想要擦肩而过,当一个人向右移动想要另外的人想过去的同时,另外的人又往他的左方向移动想要让他过去,这是处于阻塞,然后重复…