web-dev-qa-db-ja.com

同期されたメソッドは、それ自体を再帰的に呼び出します。これは壊れていますか?

この質問のポイントは、Javaが期待どおりに機能していないことを説明することです。

次のコードがどのように動作すると思いますか?

public class SynchTester {
  private static SynchTester synchTester;

  public synchronized static SynchTester getSynchTester(){
    if(synchTester==null){
      synchTester = new SynchTester();
    }

    return synchTester;
  }

  private SynchTester() {
    SynchTester myTester = getSynchTester();
  }

  public static void main(String[] args) {
    SynchTester tester = SynchTester.getSynchTester();
  }
}

再帰が完了するのを待っているデッドロックでハングすることを期待しますが、代わりにStackOverflowをスローします。明らかに同期されても、同じスレッドへのアクセスはブロックされません。

これはバグですか?

29
Thom

Javaでは、同期ロックは reentrant です。

スレッドは別のスレッドが所有するロックを取得できないことを思い出してください。 しかし、スレッドはすでに所有しているロックを取得できます。スレッドが同じロックを複数回取得できるようにすると、再入可能な同期が有効になります。これは、同期されたコードが直接的または間接的に、同期されたコードも含むメソッドを呼び出し、両方のコードセットが同じロックを使用する状況を示しています。再入可能な同期がないと、同期されたコードは、スレッドがそれ自体をブロックしないようにするために、多くの追加の予防措置を講じる必要があります。

出典: このページの下部を参照

71
Duncan Jones

同期されたメソッドは、モニターオブジェクトのロックを取得できる必要があります。モニターオブジェクトはインスタンス(または静的メソッドのクラス)です。すでにロックされているスレッドは、再度ロックを取得する必要はありません。そうです、それはスタックオーバーフロー(harhar)を引き起こす可能性があります。

2
akaIDIOT

Javaチュートリアル から:

1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまで実行を一時停止します。

したがって、syncronizedキーワードは期待どおりに機能し、同期された再帰呼び出しはJavaでは完全に合法(および機能)であると思います。

2
Luca Putzu