4.活度(Liveness)
一个并发程序能够及时执行的能力称之为活度(Liveness)。本节介绍死锁、饿死和活锁
4.1 死锁(Deadlock)
死锁描述了两个或多个线程因相互等待而永远阻塞的问题。下面举例说明。
Alphonse 和 Gaston 是好朋友,都是礼仪的信仰者。礼仪中一个严格的规则是,当你向你的朋友鞠躬时,你必须保持鞠躬状态,直到你的朋友有机会鞠躬回礼。不幸的是,这个规则没有考虑两个朋友同时相互鞠躬的情形。下面Deadlock的例子,模拟了这种情况:
public class Deadlock { static class Friend { private final String name; public Friend(String name) { this.name = name; } public String getName() { return this.name; } public synchronized void bow(Friend bower) { System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); bower.bowBack(this); } public synchronized void bowBack(Friend bower) { System.out.format("%s: %s" + " has bowed back to me!%n", this.name, bower.getName()); } } public static void main(String[] args) { final Friend alphonse = new Friend("Alphonse"); final Friend gaston = new Friend("Gaston"); new Thread(new Runnable() { public void run() { alphonse.bow(gaston); } }).start(); new Thread(new Runnable() { public void run() { gaston.bow(alphonse); } }).start(); } }
|
当运行Deadlock时,当调用bowBack时,很可能两个线程都会阻塞。没有阻塞能够结束,因为每个线程都在等待另一个结束bow。
4.2 饿死和活锁
饿死和活锁问题比死锁要碰到的少一些。但是并发程序设计者仍然会碰到的问题。
饿死
饿死是指一个线程不能够获得共享资源,因而不能够继续执行。这种情况的发生,是因为有贪婪的线程长时间占着共享资源。例如,一个对象上的同步方法要很长时间才能返回。如果一个线程频繁调用这个方法,其它需要经常访问该方法的线程就会进程被阻塞。
活锁
一个线程经常需要根据另一个线程的动作而采取相应的动作。如果另一个线程也需要根据这个线程而动作,就可能导致活锁。和死锁一样,活锁的线程也无法继续执行。但是线程并没有被阻塞——它们只是相互间忙于响应对方而无法继续进行。这就像两个人在走廊上相向相遇一样:Alphonse向左移想让Gaston通过,而Gaston也同时向右移想让Alpnonse通过。这样反复多次,他们仍然无法通过。