17

まず、サンプルを次に示します

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n", 
                    this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

私が得られないのは、閉塞がどのように発生するかです。main関数は、それぞれが独自の弓を開始する2つのスレッドを開始します。

「同期」ブロックとは正確には何ですか?同じオブジェクトに対して同じ関数が実行されていますか(私が最初に思ったように)?同じクラスのすべてのオブジェクトに同じ関数?同じオブジェクトのすべての同期された関数?同じクラスのすべてのオブジェクトのすべての同期された関数?

ここで私を助けてください。

4

4 に答える 4

29

Javaでは、それぞれObjectがスレッドをsynchronizeロックする機能を提供します。メソッドが同期されると、メソッドはそのオブジェクトインスタンスをロックとして使用します。あなたの例では、メソッドbowbowBackは両方ともsynchronizedであり、両方とも同じクラスにありFriendます。これは、これらのメソッドを実行するスレッドが、Friendインスタンス上でロックとして同期することを意味します。

デッドロックを引き起こす一連のイベントは次のとおりです。

  1. 最初のスレッドは、オブジェクト上にある、の呼び出しalphonse.bow(gaston)を開始しました。これは、スレッドがこのオブジェクトからロックを取得する必要があることを意味します。synchronizedalphonse Friend
  2. 2番目のスレッドはオブジェクト上にgaston.bow(alphonse)ある呼び出しを開始しました。これは、スレッドがこのオブジェクトからロックを取得する必要があることを意味します。synchronizedgaston Friend
  3. 開始された最初のスレッドはbowback、ロックオンgastonが解放されるのを呼び出して待機します。
  4. 開始された2番目のスレッドは、ロックオンが解放されるのを呼び出しbowbackて待機します。alphonse

イベントのシーケンスをより詳細に表示するには:

  1. main()メインのTherad(スレッド#1と呼びます)で実行を開始し、2つのFriendインスタンスを作成します。ここまでは順調ですね。
  2. メインスレッドは、最初の新しいスレッド(スレッド#2と呼びます)をコードで開始しますnew Thread(new Runnable() { ...。スレッド#2は、オブジェクト上にalphonse.bow(gaston)あるを呼び出します。したがって、スレッド#2はオブジェクトの「ロック」を取得し、メソッドに入ります。synchronizedalphonse Friendalphonsebow
  3. ここでタイムスライスが発生し、元のスレッドがさらに処理を行う機会が得られます。
  4. メインスレッドは、最初のスレッドと同じように、2番目の新しいスレッド(スレッド#3と呼びます)を開始します。スレッド#3はgaston.bow(alphonse)、オブジェクトで同期されているを呼び出しgaston Friendます。誰もgastonオブジェクトインスタンスの「ロック」をまだ取得していないため、スレッド#3はこのロックを正常に取得し、bowメソッドに入ります。
  5. ここでタイムスライスが発生し、スレッド#2はさらに処理を行う機会を得ます。
  6. スレッド#2は、のインスタンスへの参照として呼び出すようbower.bowBack(this);になりました。これは、の呼び出しと論理的に同等です。したがって、このメソッドはインスタンス上にあります。このオブジェクトのロックはすでに取得されており、別のスレッド(スレッド#3)によって保持されています。したがって、スレッド#2は、ロックオンが解放されるのを待つ必要があります。スレッドは待機状態になり、スレッド#3をさらに実行できるようになります。bowergastongaston.bowBack(alphonse)synchronizedgastongaston
  7. スレッド#3は、を呼び出すようになりました。bowbackこの場合、これは論理的には呼び出しと同じalphonse.bowBack(gaston)です。これを行うには、インスタンスのロックを取得する必要がありalphonseますが、このロックはスレッド#2によって保持されています。これで、このスレッドは待機状態になります。

これで、どちらのスレッドも実行できない位置になりました。スレッド#2とスレッド#3の両方が、ロックが解放されるのを待っています。ただし、スレッドが進行しない限り、どちらのロックも解放できません。ただし、どちらのスレッドも、ロックが解除されていないと進行できません。

したがって:デッドロック!

デッドロックは、発生するイベントの特定のシーケンスに依存することが非常に多く、再現が困難になる可能性があるため、デバッグが困難になる可能性があります。

于 2009-04-14T22:57:12.800 に答える
2

Synchronizedには2つの効果があります。

  • まず、同じオブジェクトで同期されたメソッドを2回呼び出すことはできません。1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまで実行を一時停止します。
  • 次に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しとの発生前の関係が自動的に確立されます。これにより、オブジェクトの状態への変更がすべてのスレッドに表示されることが保証されます。

つまり、同じオブジェクトで同期されたメソッドの呼び出しをブロックします。

于 2009-04-14T22:53:27.393 に答える
2

同じオブジェクトのすべての同期された関数。メソッドに「同期」のマークを付けることは、メソッドのコンテンツ全体の周りに「同期(this){」ブロックを配置することと非常に似ています。私が「同一」と言わない理由は、コンパイラが同じバイトコードを出力するかどうかがわからないためですが、定義された実行時の効果は同じです。

デッドロックは、古典的なロックの反転です。1つのスレッドがアルフォンスをロックします。次に(またはマルチコアシステムで同時に)、他のスレッドがガストンをロックします。この部分では、スレッドのスケジューリングがちょうどそのように適切なポイントでインターリーブする必要があります。

次に、各スレッドは(任意の順序でまたは同時に)他のスレッドによってすでに保持されているロックを取得しようとするため、各スレッドはスリープ状態になります。どちらも、もう一方がロックを解除するまでウェイクアップしませんが、ウェイクアップする(または終了する)までロックを解除しません。

于 2009-04-14T22:54:58.407 に答える
2

同期されたメソッドは、これらすべてのメソッドコードを

synchronized(this) {
  /// code here ...
}

ブロック。

特定のオブジェクトインスタンスoについて、一度に1つのスレッドのみがsyncized(o)ブロックを実行できます。そのブロックを実行しているスレッド(同期ロックが設定されている)がそのブロックを終了する(ロックを解除する)まで、他のすべてのスレッドは停止します。

あなたの場合、デッドロックは、Alphonseがスレッド1でお辞儀を始め、同期ブロックに入ったときに発生します。次に、スレッド1がシステムによってスワップアウトされるため、スレッド2が開始され、ガストンがお辞儀をすることができます。しかし、GastonはAlphonseで同期しているため、まだ元に戻すことはできず、スレッド1にはすでにそのロックがあります。したがって、スレッド1がそのブロックを離れるのを待ちます。次に、システムはスレッド1を元に戻し、Alphonseを元に戻そうとします。スレッド2がガストンに同期ロックを持っているためにそうすることができないことを除いて。両方のスレッドがスタックし、もう一方がお辞儀を終えるのを待ってから、お辞儀をすることができます...

于 2009-04-14T23:03:55.730 に答える