前のエントリで激しい議論が続いていますが、置いてきぼりをくらって
バーカンウターで退屈そうにしていらっしゃるお客様のために、基礎講座を。
synchronizedの基本
同時に複数の人が編集作業をすると、あっというまにぐちゃぐちゃになってしまいます。
さっきまで10と書いてあったから、そこに1を足して11を書き込もう、としたらすでに値が12になっていた…。
そこでまぁいいや、と11と書き込むと、最後になって数字の辻褄があわなかったりするわけです。
マルチスレッドでの同期の問題のひとつはここにあります。
a += 1;
という単純な式でさえ、実際にはaを読み出し、1を足し、格納するというステップから成っています。
その間に他のスレッドに読み出され、別の値を格納されたりするわけです。
「1度にいじるのはひとりだけにしてくれ!」そういいたくなりますね。たとえばここに、ボールをひとつ用意します。
このボールを手にしている人だけが、編集をできるというルールを定めましょう。
もちろん、編集が終わったらボールは返します。こうすることで、編集がちょうど並列にされることを防げます。
Javaではこれをsynchronizedブロックで表現します。
synchronized(lock) {
// 一度に一人しか触れない部分
}
synchronizedの後ろの括弧にはObjectのインスタンスを渡します。
このインスタンスが先ほどのボールの役割をします。
Objectのインスタンスであれば、なんでも構いませんが、
複数のsynchronizedブロックの同期を取る際には
おなじボールを用いるようにしなければなりません。
ボールが違えば別々に処理できてしまいます。
どことどこが一緒に処理してはいけない場所なのか考えて、ボールを配しなければなりません。
このボールをロックオブジェクトといいます。
ここでsynchronizedってメソッドの修飾子じゃなかったっけ?
という人がおられるのではないでしょうか。
メソッドの修飾キーワードとして書いた場合は暗黙にロックオブジェクトが決められます。
/** メソッドにキーワードを設定 */
public synchronized void hoge() {
// ...
}
/** synchronizedブロック */
public void hoge() {
// thisオブジェクトでのロックと同じ
synchronized (this) {
// ...
}
}
/** メソッドにキーワードを設定 */
public static synchronized void staticHoge() {
// ...
}
/** synchronizedブロック */
public void staticHoge() {
// 自身のclassオブジェクトでのロックと同じ
synchronized (Hoge.class) {
// ...
}
}
このように、インスタンスメソッドの場合はthisオブジェクトをロックオブジェクトにしたのと同じ、
staticメソッドの場合は
classオブジェクトをロックオブジェクトにしたのと同じとなります。
同期の際にはロックオブジェクトが何かを常に意識しなくてはなりません。
慣れないうちは面倒でもsynchronizedブロックで記述するようにした方がよいでしょう。
投稿日時 : 2007年8月22日 0:17