牛郎织女的幸福生活(活锁)
从前,有一对夫妻,男的叫牛郎,女的叫织女,他们很好地传承了中华民族的谦让美德,每次吃饭时都会优先考虑对方,如果对方饿的话,就让给对方吃,等对方吃饱了自己才吃,这种美德本身是好的,但是如果一味的谦让,就有可能都吃不上饭…
1. 吃饭逻辑
- 有两个就餐者,一个是牛郎,一个是织女;
- 就餐者
Diner
,有两个属性,一个是就餐人名字 name,一个是是否饥饿isHungry
- 就餐者有个行为方法
eatWith()
,他需要两个参数,一个是勺子,一个是对方,方法里面描述的是吃饭的事宜。 - 定义一个勺子类,有勺子表示就能够吃饭,里面有个属性表示所属人
owner
2. 代码实现
(1)两个就餐者开启两个线程
public class LiveLock {
public static void main(String[] args) {
Diner husband = new Diner("牛郎");
Diner wife = new Diner("织女");
Spoon spoon = new Spoon(husband);
new Thread(new Runnable() {
@Override
public void run() {
husband.eatWith(spoon, wife);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
wife.eatWith(spoon, husband);
}
}).start();
}
}
(2) 定义就餐者 Diner 类
static class Diner {
/**
* 吃饭的人
*/
private String name;
/**
* 是否饥饿
*/
private boolean isHungry;
public Diner(String name) {
this.name = name;
isHungry = true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isHungry() {
return isHungry;
}
public void setHungry(boolean hungry) {
isHungry = hungry;
}
}
(3) 定义行为方法eatWith()
/**
* 方法里面描述的是吃饭的事
*
* @param spoon 勺子
* @param spouse 夫妻对方
*/
public void eatWith(Spoon spoon, Diner spouse) {
while (isHungry) {
//如果勺子不是自己的,则等一会儿,等对方吃完
if (spoon.getOwner() != this) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
//先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃
if (spouse.isHungry) {
System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
spoon.setOwner(spouse);
continue;
}
//开始使用勺子吃饭
spoon.use();
//吃完后把自己的状态改成非饥饿状态
isHungry = false;
System.out.println(name + ": 我吃完了");
//同时把勺子直接给到对方
spoon.setOwner(spouse);
}
}
(4) 定义一个勺子类
static class Spoon {
/** 所属人owner **/
private Diner owner;
public Spoon(Diner owner) {
this.owner = owner;
}
public Diner getOwner() {
return owner;
}
public void setOwner(Diner owner) {
this.owner = owner;
}
public synchronized void use() {
System.out.printf("%s吃完了!", owner.name);
}
}
3. 代码运行结果
完整代码如下:
public class LiveLock {
public static void main(String[] args) {
Diner husband = new Diner("牛郎");
Diner wife = new Diner("织女");
Spoon spoon = new Spoon(husband);
new Thread(new Runnable() {
@Override
public void run() {
husband.eatWith(spoon, wife);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
wife.eatWith(spoon, husband);
}
}).start();
}
static class Diner {
/**
* 吃饭的人
*/
private String name;
/**
* 是否饥饿
*/
private boolean isHungry;
/**
* 方法里面描述的是吃饭的事
*
* @param spoon 勺子
* @param spouse 夫妻对方
*/
public void eatWith(Spoon spoon, Diner spouse) {
while (isHungry) {
//如果勺子不是自己的,则等一会儿,等对方吃完
if (spoon.getOwner() != this) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
//先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃
if (spouse.isHungry) {
System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
spoon.setOwner(spouse);
continue;
}
//开始使用勺子吃饭
spoon.use();
//吃完后把自己的状态改成非饥饿状态
isHungry = false;
System.out.println(name + ": 我吃完了");
//同时把勺子直接给到对方
spoon.setOwner(spouse);
}
}
public Diner(String name) {
this.name = name;
isHungry = true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isHungry() {
return isHungry;
}
public void setHungry(boolean hungry) {
isHungry = hungry;
}
}
static class Spoon {
/** 所属人owner **/
private Diner owner;
public Spoon(Diner owner) {
this.owner = owner;
}
public Diner getOwner() {
return owner;
}
public void setOwner(Diner owner) {
this.owner = owner;
}
public synchronized void use() {
System.out.printf("%s吃完了!", owner.name);
}
}
}
打印结果:
我们可以看到,程序一直重复让对方先吃,这就陷入了活锁的状态,因为程序并没有停下,还在不停打印输出,但程序一直在原地打转,没有实际前进,没有完成吃饭的动作。
5. 出现原因
其实主要问题出现在 if (spouse.isHungry)
这一句上,只要检测到对方是饥饿的话就始终谦让!
- 原因:
重试机制不变
,吃饭始终谦让 - 可以借鉴以太网的指数退避算法:连接重试时间不是固定的,而是
随机的
,并且随机范围随着碰撞强度的升高而逐渐扩大 - 处理方案:加入
随机因素
,允许某些时候不谦让
6. 解决活锁问题
我们只要在 if (spouse.isHungry)
这一句的判断条件加一个随机因素 (random.nextInt(10) < 9) 即可:
/**
* 方法里面描述的是吃饭的事
*
* @param spoon 勺子
* @param spouse 夫妻对方
*/
public void eatWith(Spoon spoon, Diner spouse) {
while (isHungry) {
//如果勺子不是自己的,则等一会儿,等对方吃完
if (spoon.getOwner() != this) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
Random random = new Random();
//先检查对方是否饥饿,如果对方饥饿的话先把勺子交给对方,让对方先吃,random.nextInt(10) < 9 表示有十分之一的概率不谦让
if (spouse.isHungry && random.nextInt(10) < 9) {
System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧");
spoon.setOwner(spouse);
continue;
}
//开始使用勺子吃饭
spoon.use();
//吃完后把自己的状态改成非饥饿状态
isHungry = false;
System.out.println(name + ": 我吃完了");
//同时把勺子直接给到对方
spoon.setOwner(spouse);
}
}
打印结果:
可以看到他们在谦让了多次之后,织女终于吃到饭了,吃饱了之后勺子给了牛郎,牛郎也吃完了,然后就过上了没羞没臊的幸福生活…