常に最初の手法を使用してメソッドを呼び出す場合、2 つのメソッドは実際にはアトミックです。一度に 1 つのスレッドのみがブロックとして実行されます (method1 を実行し、method2 を実行します)。
2 番目のケースでは、任意の のインスタンスがB
2 つのメソッドをアトミックに呼び出しますが、2 つのスレッドがそれぞれ の個別のインスタンスを持っている場合B
、method1()
との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