2009/05/07
Javaのオートボクシングでバグ寸前
Javaの同期処理の説明のために以下のようなコードを使いました。synchronizedを説明するため以外に意味のあるコードではありません。
class MyWorker implements Runnable {
private static final int LOOP_NUM = 10000000;
private MyCounter counter;
MyWorker(MyCounter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < LOOP_NUM; i++) {
counter.increment();
}
}
}
public class MyCounter {
private int count;
public synchronized void increment() {
count++;
}
public void doit() throws InterruptedException {
Thread t1 = new Thread(new MyWorker(this));
t1.start();
Thread t2 = new Thread(new MyWorker(this));
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
public static void main(String[] args) throws InterruptedException {
MyCounter my = new MyCounter();
my.doit();
}
}
この後、synchronizedには別の書き方もあるという説明をするため、countの型が参照型のIntegerなら次のようにも書ける、と展開しようと思いました。
public class MyCounter {
private Integer count = 0;
public void increment() {
synchronized(count) {
count++;
}
}
以下省略
分かる人には一瞬でしょうが、これは期待どおりの同期処理をしません。答えを直接書くのは野暮なので、分からない人にヒントだけ書きます。Integerは不変型なのでcount++でオブジェクトの状態を変更していません。
以前(http://dev.ariel-networks.com/Members/inoue/terror-bugs2)もオートボクシングで恐いバグを経験しましたが、オートボクシングはJavaに気づきにくいバグをもたらします。
FindBugsを通してみると、 http://findbugs.sourceforge.net/bugDescriptions.html#DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE
DL: Synchronization on boxed primitive could lead to deadlock
の警告を出してきました。
もしふたつのスレッドが次のコードを実行するとデッドロックする可能性があります。まあ、こんなコードは書きませんが。
// スレッド1
Integert i1 = 1;
synchronized(i1) {
increment();
}
// スレッド2(MyCounterクラスのメソッドを仮定)
Integert i0 = 0;
synchronized(i0) {
count = 1;
increment();
}
Javaで試験問題を作る時はFindBugsを参考にするのがよさそうです。
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/terror-bugs3/tbping