活性(Liveness)
并发程序能及时(in a timely manner)执行的能力叫作它的活性。本节描述了最常见的一种活性问题:死锁(deadlock)。然后简单描述了另外两个活性问题, 饥饿(starvation)和活锁(livelock)。
死锁
死锁描述了两个或更多的线程因为要等待彼此释放锁而永远阻塞。这里有个例子:
Alphonse和Gaston是朋友,很重视礼节(great believers in courtesy)。一个很重要的约定是,当向一个朋友鞠躬时,如果朋友不向你回礼(鞠躬)你就不能直身。不幸的是,如果两个人同时向对方鞠躬就有问题了。在本例中,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();
}
}
当死锁发生时,很可能是这样的情形:两个线程都因试图调用对方的bowBack而阻塞。两个阻塞都不会停止,因为每个线程都在等待对方停止对bow的调用。(译者注:若双方行为结束的条件是对方结束行为,那么双方的行为都将无限等待下去。)
饥饿(Starvation) and 活锁(Livelock)
Starvation 和 Livelock 与死锁相比较而言是非常少见的活性问题,但并发软件的设计者依然会遇到。
饥饿
Starvation描述了一种情形,一个线程没有机会获得共享资源也无法取得进展。如果有“贪婪”的线程长期占有共享资源,这就有可能发生。例如,假设一个对象提供了一个需要很长时间才能退出的同步方法。如果某一线程经常调用此方法,那其他调用该对象其他同步方法或以该对象作为锁提供者的同步代码,就会经常阻塞。
活锁
若有两个线程,它们的操作都需要对另一线程的操作做出回应,此时活锁会发生。就像死锁一样,活锁线程无法取得进展。然而,线程不是阻塞的——它们只是因为太忙于向彼此回复而无暇工作。这就像(This is comparable to)两个人在走廊里都试图给对方让路(pass each other):Alphonse 移到他的左边让Gaston过,而Gaston移到他的右边让Alphonse过,当看到他们仍在阻塞对方,Alphone移到他的右边,而Gaston移到他的左边。他们仍在阻塞,然后…
(以下是译者加的活锁例子)
package com.test.livelock;
public class Gentleman extends Thread {
private String manName;
private Gentleman anotherGentleman;
public Gentleman(String name,Gentleman anotherGentleman){
this.manName=name;
this.anotherGentleman=anotherGentleman;
}
public Gentleman() {
}
void walkwith(Gentleman anotherGentleman){
try {
System.out.println(manName+" says :i pass "+anotherGentleman.getManName());
anotherGentleman.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(manName+" says : i passed");
}
private String getManName() {
return manName;
}
public Gentleman getAnotherGentleman() {
return anotherGentleman;
}
public void setAnotherGentleman(Gentleman anotherGentleman) {
this.anotherGentleman = anotherGentleman;
}
public void setManName(String manName) {
this.manName = manName;
}
@Override
public void run() {
walkwith(anotherGentleman);
}
public static void main(String[] args) {
Gentleman jay=new Gentleman();
Gentleman jj=new Gentleman();
jay.setAnotherGentleman(jj);
jj.setAnotherGentleman(jay);
jay.setManName("Jay");
jj.setManName("JJ");
jay.start();
jj.start();
}
}