「//fix this/」の記事インデックス

//fix this/ Javaコードの間違いをさがせ!


このシリーズは、あなたのコーディング・スキルを試すコーナーです。毎号オラクルのJavaエバンジェリストたちが、Javaコードに関する難問を出題し、正解と解説を掲載します。是非、毎号チャレンジして貴方のコーディング能力の向上にお役立て下さい。


第10回目 (Java Magazine 2013/Sept-Octからの出題)


1. 今回のテーマ 「クラスの初期化」 

あるプログラマーが次のコードに取り組んでいます。このコードはたいていデッドロックになり、ときどき NullPointerException をスローしますが、最後まで正常に実行されることはありません。どのようにすればこの問題を解決することができるでしょうか?



2. コード

 

final class App {
 public static final Integer RETRIES = 3;
 private static final App instance = new App(Controller.getInstance())
 private Controller ctrl;
 public App(Controller controller) { this.ctrl = controller; }
 public void run() { ctrl.run(); }
 public static App getInstance() { return instance; }
}

final class Controller {
 private static final Controller instance = new Controller(App.RETRIES);
 private final Integer retries;

 private Controller(Integer retries) { this.retries = retries; }
 public static Controller getInstance() { return instance; }
 public void run() { }
 public Integer getRetries() { return retries; }
}

/...

// some thread starts running the app
new Thread(new Runnable() {
 public void run() { App.getInstance().run(); }
}, "AppThread").start();

// meanwhile, on the main thread
Controller.getInstance().getRetries()

3.正しいのはどれ?

上記のコードの正しい修正方法を選んでください。

1)RETRIES に対して、Integer の代わりに int を使用する。

2)jstack を使用してデッドロックを見つける。

3)RETRIES を Controller の中に移動する。

4)インラインの App.RETRIES に対して -XX:CompileThreshold=0 を使用する。

<ヒント>
シンプルにすることが肝心






【答え】

正解は 1)と 3)の両方です。問題の根本原因は、2つの異なるスレッドからクラスローダーを呼び出せるため、デッドロックが発生することにあります。

・App スレッドから App を参照しているため、このクラスのロードと 初期化が必要です。しかし、この初期化の処理中に Controller をロー ド(および初期化)する必要があります。

・このクラスはすでに main からロードしているため、main スレッドは App スレッドによる App クラスのロード / 初期化を待機しますが、同 時に App スレッドは main による Controller クラスの初期化を待機し ます。

Integer を int に変更すると、コンパイラは RETRIES を定数とみなしてイン ライン展開するため、2 つのクラス間にある依存関係が削除されます。ま た、RETRIES を Controller 内に移動しても(RETRIES は Controller クラス 内でのみ参照されているため、本来このクラス内にあるべき)、この依存 関係が再び壊れます。


<< 第9回へ戻る I 第11回へ進む >>

「//fix this/」の記事インデックス