0

この単純なサンプルでデッドロックが発生する理由がわかりません。何が問題なのですか?

public static void main(String[] args) {
        Object data = null;
        new Thread(new Producer(data)).start();
        new Thread(new Consumer(data)).start();
    }
}

class Producer implements Runnable {
    private Object data;

    public Producer(Object data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (true) {
            while (data != null) {}
            data = new Object();
            System.out.println("put");
        }
    }
}

class Consumer implements Runnable {
    private Object data;

    public Consumer(Object data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (true) {
            while (data == null) {  }
            data = null;
            System.out.println("get");
        }
    }
4

5 に答える 5

3

2 つの問題があります。

1: 2 つの個別の Runnables があり、それぞれが data という名前の独自のプライベート内部メンバーを持っています。一方に加えられた変更は、他方には表示されません。2 つのスレッド間でデータを渡したい場合は、両方がアクセスできる共通の場所にデータを格納する必要があります。また、アクセスを同期するか、参照を揮発性にする必要があります。

2: 小切手が反転しているようです。null でない場合は null にして、null の場合は作成したいですか? 実際にそこで何をしたいのかを伝えるのは難しいです!:)

public static volatile Object data;

public static void main(String[] args) {
        data = null;
        new Thread(new Producer(data)).start();
        new Thread(new Consumer(data)).start();
    }
}

class Producer implements Runnable {

    public Producer(Object data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (true) {
            while (data == null) {}
            data = new Object();
            System.out.println("put");
        }
    }
}

class Consumer implements Runnable {

    public Consumer(Object data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (true) {
            while (data != null) {  }
            data = null;
            System.out.println("get");
        }
    }

(また、これは実際には、デッドロックとして定義する古典的な例ではありません。2 つのスレッドが、もう一方のロックを必要としているために処理を進めることができません。ここにはロックはありません。これは、2 つの無限ループの例です。何もしないでください。)

于 2012-04-04T17:46:30.940 に答える
2

各インスタンスには独自のdataフィールドがあります。
コンシューマーは、プロデューサーの変更を見ることはありません。

于 2012-04-04T17:44:09.660 に答える
2

コンシューマとプロデューサには別々のデータフィールドがあるため、コンシューマは消費するデータを取得しません。

また、フィールドでコンシューマー/プロデューサーをスピンロックすることは、一般的には良い考えではありません.ミューテックスまたはセマフォを使用して、データの可用性/公開の可能性を通知する方がはるかに優れています. これが知識を探すためのテスト以上のものである場合は、これら2つの使用方法を実際に読む必要があります.

于 2012-04-04T17:44:53.453 に答える
1

生成されたものが「生成」するとき、それは新しいオブジェクトへの独自のdata参照を指すだけであり、消費者は何が起こったのかを知る方法がありません. 代わりにできることは、別のクラスを作成することです

class Data {
    private Object data = null;
    synchronized void set( Object data ){ this.data = data; }
    synchronized Object get(){ return data; }
}

次に、メインで行う

Data data = new Data();

「データ」オブジェクトをコンシューマーとプロデューサーに渡し、割り当ての代わりに get/set メソッドを使用します。

そうすれば、消費者と生産者の両方が同じDataオブジェクトを指し、生産者が生成するか消費者が消費するときに、共有している Data オブジェクトの参照が変更されます。

于 2012-04-04T17:51:30.943 に答える
1

私はこれがあなたが意図したことを行うべきだと思います(それはまだ悪いコードです):

public class Example {
    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        new Thread(new Producer(consumer)).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {
    private final Consumer consumer;

    public Producer(Consumer consumer) {
        this.consumer = consumer;
    }

    @Override
    public void run() {
        while (true) {
            while (consumer.data != null) {}
            consumer.data = new Object();
            System.out.println("put");
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    public volatile Object data;

    @Override
    public void run() {
        while (true) {
            while (data == null) {}
            data = null;
            System.out.println("get");
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

例の主なエラー (個別のデータ フィールド) は非常に基本的なものであるため、並列プログラミングなどの高度なトピックに進む前に、Java の基本に焦点を当てる必要があると思います。

于 2012-04-04T18:00:41.433 に答える