用餐Philosopher的解决方案最终陷入僵局

时间:2016-11-26 06:55:15

标签: java multithreading dining-philosopher

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

2 个答案:

答案 0 :(得分: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()相同的情况。所以你仍然需要管理我们有相同价值观的案例。

正确管理同等价值的方法是使用名为&#34; 打破&#34;的第三个锁定。锁定

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(),因为这样您赢了&#39 ; t取决于特定目标对象的类型。

来自Java Concurrency in PracticeBrian Goetz 10.1.2动态锁定顺序死锁一章中的更多详细信息

答案 1 :(得分:3)

BUG就是你有

{{1}}

什么时候应该

{{1}}

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