1

我已经为餐饮哲学家的问题实现了资源层次结构解决方案。当我尝试比较两个筷子的 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();
        }
    }
}
4

2 回答 2

5

您的问题与您没有管理案例有关,不幸的是,您使用left.n == right.n的不是用 初始化Chopstick数组,而是只有没有正确管理的类型案例,因此您会遇到死锁。sticks[i] = new Chopstick(i)sticks[i] = new Chopstick(n)left.n == right.n

由于您没有覆盖该方法hashCode(),因此使用hashCode()有助于避免该问题,因为它们是Chopstick具有不同值的不同实例,hashCode()但您仍然可能遇到我们有 2 个具有相同值的不同实例的Chopstick情况hashCode()。因此,您仍然必须管理我们具有相同价值的情况。

正确管理相等值的方法是使用第三个锁,称为“打破平局”锁

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()因为这样你就不会依赖于目标对象类型的特定内容。

更多细节见Brian Goetz的10.1.2 Java Concurrency in Practice动态锁顺序死锁

于 2016-11-26T09:24:53.847 回答
3

BUG是你有

sticks[i] = new Chopstick(n); 

什么时候应该

sticks[i] = new Chopstick(i);

即使对象的数据相同,对象的哈希值仍然是唯一的,因为您没有覆盖 hashCode 函数。

于 2016-11-26T08:23:03.400 回答