読者です 読者をやめる 読者になる 読者になる

mizdra's blog

ぽよぐらみんぐ

【Java】synchronizedの動作確認

お久しぶりです。mizdraです。
今回はJavaでスレッドプログラムを作っている際にsynchronizedの動作について興味を持ったのでサンプルプログラムを作成してみました。

環境はJDK 1.8.0_25です。

synchronizedの排他制御

スレッドはロックオブジェクトのロックを取得してsynchronizedブロックを実行します。
また、そのブロックの処理が終わるまで、他のスレッドが同じロックオブジェクトのロックを入手することを禁止します。

スレッドがロックを解放するには、ブロックの処理が終了するか、waitメソッドなど一部の特別なメソッドを実行するかなどの方法があります。*1

出力
A
A
A
A
A
A
A
...

Aクラスではsynchronizedブロックを使って同期処理をしていて、BクラスではAクラスのprintCharメソッドを呼び出しています。

結果からaインスタンスがobjのロックを所持しているので、bインスタンスがSystem.out.println(value);を実行できていないことが分かるかと思います。

synchronizedのブロックとメソッドの違い

synchronizedは戻り値の型宣言の直前に置くことでメソッド自体にも付けらます。
synchronizedメソッドはsynchronizedブロックと以下の点で異なります。

出力
A
A
D
D
D
C
C
...

AクラスとCクラス、BクラスとDクラスはそれぞれ機能的にほぼ等しいです。*2


synchronizedメソッドの罠

synchronizedメソッドはロックオブジェクトがそのメソッドのクラスのインスタンス、もしくはクラスとなるので、そられの内部で定義している複数のsynchronizedメソッドを同時に実行することは出来ません。
つまり、各メソッドは別々のロックオブジェクトを所持するのではなく、インスタンス、もしくはクラス単位で同じものを所持するということになります。

出力
A
A
D
D
D
A
A
...

DクラスはAクラスを継承しているクラスなだけで別々のインスタンスなので、A, Dが出力されます。
一方、B, Cクラスはaインスタンスメソッドを実行しているのでB, CCCは出力されていません。

感想

排他制御って難しい。
日本語も難しい。

*1:基本的にはAPIドキュメントにそれらの特別なメソッドがロックを解放することが書かれているので、必要に応じて参照すべし。

*2:実際にはコンパイル時に生成される中間コードの内容が少し異なる