2016-10-25 3 views
2

マルチスレッドのシナリオでDeadlockを記述したOracleのJavaチュートリアルで、exampleが見つかりました。System.out.formatとSystem.out.printlnによる突き合わせ

は、したがって、この例では、私はそう、これらの変更にデッドロックを発生させずに正常に終了し、プログラムを実行する際のライン17とライン18

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) { 
     //My Changes 
     //System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); //Line 17 
     System.out.println(this.name + ": " + bower.getName() + " has bowed to me!"); //Line 18 
     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() { 
     @Override 
     public void run() { 
      alphonse.bow(gaston); 
     } 
    }).start(); 

    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      gaston.bow(alphonse); 
     } 
    }).start(); 
    } 
} 

での変更と印刷された次の出力

Alphonse: Gaston has bowed to me! 
Gaston: Alphonse has bowed back to me! 
Gaston: Alphonse has bowed to me! 
Alphonse: Gaston has bowed back to me! 

を以下の作っそれはなぜこのように振る舞ったのですか? printlnステートメントはどのようにデッドロックを防止しましたか?

+0

私はそれがどうなるかわかりません。 'System.out.format'はロックに関して' System.out.println'とは何も変わりません。 –

+2

それは違いはありません。デッドロックはスレッドインターリーブに依存します。スレッドインターリーブは実行間で異なります。 System.formatで数回実行すると、時々正しい出力が得られるでしょう。そしてprintlnを使うと、プログラムがデッドロックしている場所での実行も見えます。 – assylias

+0

確かに:最初の変種は[ideoneでうまく動作する](http://ideone.com/bV6nd8)。 –

答えて

5

System.out.printSystem.out.formatのどちらを使用するかは、基本的に同じことをしています。 Gaston.bow(Alphonse)の実行がAlphonse.bow(Gaston)及びbower.bowBack(Alphonse)(またはその逆)の開始との間に開始された場合

デッドロックがここで起こる:2つのスレッドが他に保持されたモニタを待っているので、デッドロックが発生します。デッドロックがないように見えますので、Gaston.bowAlphonse.bowと完全bower.backBack(Alphonse)が実行されている可能性がある - それは、スレッドがスケジュールされているかに応じて、微妙なタイミングの問題に依存しているため

これは、一貫性が起こります。

この問題を解決するための古典的な方法は、まず、同じロックが最初にするたびに取得されるように、ロック取得を注文することです。これは、デッドロックの可能性を防ぐ:

public void bow(Friend bower) { // Method no longer synchronized. 
    int firstHash = System.identityHashCode(this); 
    int secondHash = System.identityHashCode(bower); 

    Object firstMonitor = firstHash < secondHash ? this : bower; 
    Object secondMonitor = firstHash < secondHash ? bower : this; 
    synchronized (firstMonitor) { 
    synchronized (secondMonitor) { 
     // Code free (*) of deadlocks, with respect to this and bower at least. 
    } 
    } 
} 

(*)System.identityHashCodeが異なるオブジェクトに同じ値を返すことができますので、それは、デッドロックフリーされることが保証かなりではありません。それは合理的に起こりそうもない。

Birthday paradoxのアプリケーションです:モニターが2台しかない場合、衝突する可能性は10^-18です。 77kを超えるモニターを使用した場合、衝突が起こる可能性は高くなります。

4

あなたはここで物を混ぜています。

あなたは、各デッドロックとそのコードが実行される任意の時間を受け取ることをコードの作品は必ずしも意味しないデッドロックな状況につながることができるという事実。

これは、マルチスレッドのような難題を作り出す面の1つです。コードを1回、10回、100回実行するとすべてが「うまくいく」ということです。次回に失敗する可能性はまだあります。

言い換えれば、最も外側のレベルのループにそのコードを入れてみてください。遅かれ早かれ(おそらくもっと早い;あなたがあまり「スリープしない」場合)、デッドロックを起こすべきです!

物事は簡単で、デッドロックが簡単に、我々はマルチスレッドに対処する方法をすべてのそれらの書籍や図書館やアイデアを必要としないことを検出することができたなら...

1

デッドロックがに依存しませんprintln関数はまったくありません。これは、2つのスレッドがお互いにアクセスし、互いにロックしようとしていることが原因です。

フォーマットからprintlnへの変更は、スレッドが衝突することなく、すなわちデッドロックすることを可能にするのに十分なレイテンシをプログラムにもたらしました。あなたは本当にそれを修正していません。スレッドがデッドロックしないことを意味するレイテンシを追加しました。

3

まだほとんど間違いなくデッドロックを私はループ内のコードを実行し、それが100回の試行のうち、82回をデッドロック、いくつかの実証と、ここでの回答の残りの部分をサポートするので、あなたのコードに。

+0

私はそれに同意しません。問題は「printlnステートメントがデッドロックをどのようにして防止したか」ということです。私の答えはそうではないということです。私はそれが他の答えを参照しているので、私自身が答えとして立っていないことに同意することができます – Raniz

+0

意味が分かります;-) – GhostCat

関連する問題