1

食事の哲学者の問題に対するリソース階層ソリューションを実装しました。2 つの Chopsticks の n 値を比較しようとすると、デッドロックになります。ただし、n 値の代わりに hashCodes を使用すると、スムーズに実行されます。この違いはなぜですか?どっちも結局数字じゃないの?

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初期化する代わりに、適切に管理されていないタイプのケースのみを使用してデッドロックが発生するケースを管理していないという事実に関連しています。Chopsticksticks[i] = new Chopstick(i)sticks[i] = new Chopstick(n)left.n == right.n

メソッドをオーバーライドしなかったためhashCode()、 を使用すると、 の値が異なる の異なるインスタンスでhashCode()あるため、問題を回避するのに役立ちます。そのため、同じ値を持つケースを管理する必要があります。ChopstickhashCode()ChopstickhashCode()

等しい値を適切に管理する方法は、「タイ ブレーク」ロックと呼ばれる 3 番目のロックを使用することです。

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 章の動的ロック順序デッドロックJava 同時実行の実践( Brian Goetz著) を参照してください。

于 2016-11-26T09:24:53.847 に答える
3

バグはあなたが持っていることです

sticks[i] = new Chopstick(n); 

いつあるべきか

sticks[i] = new Chopstick(i);

hashCode 関数をオーバーライドしていないため、オブジェクトのデータが同じであっても、オブジェクトのハッシュ値は一意のままです。

于 2016-11-26T08:23:03.400 に答える