0

同期メソッドを含むクラスがあります:

class A {
    synchronized void method1() {};
    synchronized void method2() {};
}

一部のクライアントクラスが呼び出したい場合、それらの呼び出しがアトミックであることを知っていmethod1()ますmethod2()

最初のケース:

class B {    
    A a;    
    public void foo() {    
        synchronized(a) {
            a.method1();
            a.method2();
        }    
    }

}

2 番目のケース:

 class B {    
        A a;

        final Object lock = new Object();

        public void foo() {    
            synchronized(lock) {
                a.method1();
                a.method2();
            }    
        }

    }

あるケースを使用して別のケースを使用しない理由はありますか?

4

2 に答える 2

1

おそらく、a で同期するのが最も理にかなっています。これは、少なくとも「最小の驚き」の原則を維持しています。a は、他のクライアントも同期する最も自然なオブジェクトです。それを明確にするためにいくつかのドキュメントを追加してください。さらに、A の同期メソッドでロックを取得することが保証されます。これは、A と B で異なるロックを使用している場合、競合する可能性のあるロックよりもわずかに効率的である可能性があります。

于 2013-05-24T19:56:24.310 に答える
0

常に最初の手法を使用してメソッドを呼び出す場合、2 つのメソッドは実際にはアトミックです。一度に 1 つのスレッドのみがブロックとして実行されます (method1 を実行し、method2 を実行します)。

2 番目のケースでは、任意の のインスタンスがB2 つのメソッドをアトミ​​ックに呼び出しますが、2 つのスレッドがそれぞれ の個別のインスタンスを持っている場合Bmethod1()とのmethod2()呼び出しはインターリーブできます。想像:

  • スレッド 1 が所有B b1 = new B(a)
  • Thread2 が所有B b2 = new B(a)(同じインスタンスに対してa)
  • Thread1 が を呼び出しb1.newB()、ロックを取得します。b1.lock
  • Thread2 が を呼び出しb2.newB()、ロックを取得しますb2.lock(これは とは別のオブジェクトであるため、競合しませんb1.lock) 。
  • スレッド 1 の開始a.method1()
  • Thread2 は開始しようとしa.method1()ますが、同期されているためブロックされます
  • スレッド 1 終了a.method1()
  • スレッド 2 の開始a.method1()
  • a.method2()スレッド2 がまだ実行されているときにスレッド1 が開始しますmethod1()
    • 原子性が達成されていません!

static finalを作成することでこれに対処できますB.lockが、これは必要以上にロックする可能性があります。現在、 のすべての呼び出し{A.method1(); A.method2();}は、 のインスタンスごとにシリアル化されるのではなく、シリアル化されていますA

この方法で最初の同期を使用したとしても、他のすべての人がうまくやっていることに翻弄されます。他の誰かがa.method1()( 経由ではなく) 直接呼び出した場合B.fooでも、非アトミック インターリーブを取得できます。

  • スレッド 1 が所有B b1 = new B(a)
  • Thread2 がa直接所有 (の同じインスタンスa)
  • Thread1 が を呼び出しb1.newB()、ロックを取得します。b1.lock
  • スレッド 1 の開始a.method1()
  • Thread2 は開始しようとしa.method1()ますが、同期されているためブロックされます
  • スレッド 1 終了a.method1()
  • スレッド 2 の開始a.method1()
  • a.method2()スレッド2 がまだ実行されているときにスレッド1 が開始しますmethod1()
    • 原子性が達成されていません!

method1()との可視性を制限する以外に、これについてできることはあまりありませんmethod2()。たとえば、それらをパッケージプライベートまたは保護することができ、メソッドにアクセスできるクラスがその権限を悪用するよりもよく知っていると想定/期待できます。そのルートに行く場合は、同期する必要さえありません。method1()または--がロックされている間に呼び出されることmethod2()を文書化し、想定する (必要に応じて をアサートする) こともできます。this

于 2013-05-24T19:57:34.760 に答える