//fix this/ Javaコードの間違いをさがせ!
このシリーズは、あなたのコーディング・スキルを試すコーナーです。毎号オラクルのJavaエバンジェリストたちが、Javaコードに関する難問を出題し、正解と解説を掲載します。是非、毎号チャレンジして貴方のコーディング能力の向上にお役立て下さい。
Articles
Java
Java技術関連記事
Fix This
このシリーズは、あなたのコーディング・スキルを試すコーナーです。毎号オラクルのJavaエバンジェリストたちが、Javaコードに関する難問を出題し、正解と解説を掲載します。是非、毎号チャレンジして貴方のコーディング能力の向上にお役立て下さい。
あるプログラマーが次のコードに取り組んでいます。このコードはたいていデッドロックになり、ときどき NullPointerException をスローしますが、最後まで正常に実行されることはありません。どのようにすればこの問題を解決することができるでしょうか?
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()
上記のコードの正しい修正方法を選んでください。
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 クラス 内でのみ参照されているため、本来このクラス内にあるべき)、この依存 関係が再び壊れます。