用餐哲学家的解决方案陷入僵局
Dining Philosopher's solution ending up in deadlock
我已经为哲学家就餐问题实施了资源层次结构解决方案。当我尝试比较两个 Chopsticks 的 n 值时,它们最终陷入僵局。但是,如果我使用他们的 hashCodes 而不是 n 值,它会顺利运行。为什么会有这种差异?他们不都是一天结束时的数字吗?
import java.util.Random;
class Chopstick {
public final int n;
public Chopstick(int n) {
this.n = n;
}
}
class Philosopher extends Thread {
private Chopstick left, right;
private Random random;
private final int n;
public Philosopher(int n, Chopstick left, Chopstick right) {
this.n = n;
if (left.n > right.n) { // no deadlock if I replace this with left.hashCode() > right.hashCode()
this.right = left;
this.left = right;
} else {
this.left = left;
this.right = right;
}
this.random = new Random();
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(random.nextInt(10)); // Think
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
class Main {
public static void main(String[] args) {
final int n = 3;
Chopstick[] sticks = new Chopstick[n];
Philosopher[] ps = new Philosopher[n];
for (int i = 0; i < n; i++) {
sticks[i] = new Chopstick(n);
}
for (int i = 0; i < n; i++) {
ps[i] = new Philosopher(i, sticks[i], sticks[(i + 1) % n]);
ps[i].start();
}
}
}
BUG是你有
sticks[i] = new Chopstick(n);
什么时候应该
sticks[i] = new Chopstick(i);
对象的哈希值仍然是唯一的,即使它们的数据相同,因为您没有覆盖 hashCode 函数。
您的问题与以下事实有关:您没有管理 left.n == right.n
的情况,不幸的是,您没有使用 sticks[i] = new Chopstick(i)
初始化 Chopstick
数组,而是使用了 sticks[i] = new Chopstick(n)
这样你就只有 left.n == right.n
类型的案例,这些案例没有得到妥善管理,所以你会遇到死锁。
由于您没有覆盖方法 hashCode()
,因此使用 hashCode()
有助于避免此问题,因为它们是 Chopstick
的不同实例,具有不同的 hashCode()
值但是您仍然可能遇到这样的情况,即我们有 2 个不同的 Chopstick
实例和相同的 hashCode()
。所以你仍然需要处理我们有相同价值观的情况。
正确管理相等值的方法是使用称为“tie breaking”的第三个锁
class Philosopher extends Thread {
// The tie breaking lock
private static Object tieLock = new Object();
...
private void printAndSleep() throws InterruptedException {
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
public void run() {
...
if (left.n == right.n) {
// Equal values so we need first to acquire the tie breaking lock
synchronized (tieLock) {
printAndSleep();
}
} else {
printAndSleep();
}
...
}
}
一种更通用的管理锁顺序的方法是依靠 System.identityHashCode(obj)
作为每个实例的值来排序,而不是使用字段的值或 hashCode()
,因为这样你就不会依赖特定于目标对象类型的内容。
的 10.1.2 动态锁顺序死锁 一章中有更多详细信息
我已经为哲学家就餐问题实施了资源层次结构解决方案。当我尝试比较两个 Chopsticks 的 n 值时,它们最终陷入僵局。但是,如果我使用他们的 hashCodes 而不是 n 值,它会顺利运行。为什么会有这种差异?他们不都是一天结束时的数字吗?
import java.util.Random;
class Chopstick {
public final int n;
public Chopstick(int n) {
this.n = n;
}
}
class Philosopher extends Thread {
private Chopstick left, right;
private Random random;
private final int n;
public Philosopher(int n, Chopstick left, Chopstick right) {
this.n = n;
if (left.n > right.n) { // no deadlock if I replace this with left.hashCode() > right.hashCode()
this.right = left;
this.left = right;
} else {
this.left = left;
this.right = right;
}
this.random = new Random();
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(random.nextInt(10)); // Think
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
class Main {
public static void main(String[] args) {
final int n = 3;
Chopstick[] sticks = new Chopstick[n];
Philosopher[] ps = new Philosopher[n];
for (int i = 0; i < n; i++) {
sticks[i] = new Chopstick(n);
}
for (int i = 0; i < n; i++) {
ps[i] = new Philosopher(i, sticks[i], sticks[(i + 1) % n]);
ps[i].start();
}
}
}
BUG是你有
sticks[i] = new Chopstick(n);
什么时候应该
sticks[i] = new Chopstick(i);
对象的哈希值仍然是唯一的,即使它们的数据相同,因为您没有覆盖 hashCode 函数。
您的问题与以下事实有关:您没有管理 left.n == right.n
的情况,不幸的是,您没有使用 sticks[i] = new Chopstick(i)
初始化 Chopstick
数组,而是使用了 sticks[i] = new Chopstick(n)
这样你就只有 left.n == right.n
类型的案例,这些案例没有得到妥善管理,所以你会遇到死锁。
由于您没有覆盖方法 hashCode()
,因此使用 hashCode()
有助于避免此问题,因为它们是 Chopstick
的不同实例,具有不同的 hashCode()
值但是您仍然可能遇到这样的情况,即我们有 2 个不同的 Chopstick
实例和相同的 hashCode()
。所以你仍然需要处理我们有相同价值观的情况。
正确管理相等值的方法是使用称为“tie breaking”的第三个锁
class Philosopher extends Thread {
// The tie breaking lock
private static Object tieLock = new Object();
...
private void printAndSleep() throws InterruptedException {
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
public void run() {
...
if (left.n == right.n) {
// Equal values so we need first to acquire the tie breaking lock
synchronized (tieLock) {
printAndSleep();
}
} else {
printAndSleep();
}
...
}
}
一种更通用的管理锁顺序的方法是依靠 System.identityHashCode(obj)
作为每个实例的值来排序,而不是使用字段的值或 hashCode()
,因为这样你就不会依赖特定于目标对象类型的内容。