Hatena::ブログ(Diary)

虎塚 このページをアンテナに追加 RSSフィード Twitter

2009-07-20

メモ:トランザクションを実現するインターセプタ

上で公開されているプレゼン資料を拝見していて、たどり着いた次の記事。

特定のメソッドをトランザクションの対象外にする設定の方法とか、超わかりやすいです! ありがたや。ありがたや。

第5章 Singleton - 増補改訂版Java言語で学ぶデザインパターン入門

第5章は、Singletonパターン。

今回のサンプルコードは必要最小限の長さなので、写経は楽勝。でもSingletonって内容は重い。GCとマルチスレッドを理解する必要がある。そして、私はそれ以前の問題であるクラスローダの仕組みを理解していなかったせいで(情けない…)、そっちを勉強するのにも時間がかかってしまった…。それはそうと、Singletonパターンの復習メモです。

Singletonを使う利点

生成されるインスタンスの数を、ただ一つに限定することができます。

Template MethodやFactory Methodと同じく、Singletonでもインスタンスを直接生成しません。クラスにpublicなメソッド getInstance() を用意します。Singletonパターンを使うクラスは、自分のインスタンスを private static なフィールドで保持しています。なので、getInstance()は、戻り値としてそのフィールド(変数)を返します。これでSingletonのただ一つのインスタンスを取り出すことができます。

クラス外からインスタンスを直接生成されないように、コンストラクタをprivateにしておくことがポイントです。

唯一のインスタンスが生成されるタイミング

本文では、クラスの初期化の参考文献として『Java言語仕様 第3版 (The Java Series)』が紹介されています。そんな難しい本、もってないよー。というわけで、Webで確認。

クラスがロードされて初期化された時に、staticフィールドも初期化されます。Singletonクラスのロード、つまり、クライアントのコードからgetInstance()が呼び出された時に、Singletonのただ一つのインスタンスは生成されます。

複数のSingletonインスタンスができないよう気をつける

問題5-3。singletonが遅れてインスタンス化される場合は、スレッドセーフにしましょうという話。

    // 危ないサンプル。synchronizedが必要。
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

Javaの格言―より良いオブジェクト設計のためのパターンと定石』のp.224で、ほぼ同じコードを使ってこの問題が解説されています。singletonインスタンスのnullチェックからSingletonのコンストラクタ呼出しまでのわずかな時間に、他のスレッドから (singleton == null) が再評価されると、条件文の値がtrueになってしまいます。すなわち、Singletonのインスタンスが、さらに1つ生成されます。

解決策は、getInstance()をスレッドセーフにすることです。

孤立したSingletonがGCの対象にならないよう気をつける

こっちは本の内容から逸れるけど、大事なので復習。Singletonパターンで生成された唯一のインスタンスは、孤立したとき、GCの対象となる可能性があります。この解決策として、前述の『Javaの格言』p.p.208-209で2つの方法が挙げられています。

  • プログラムの起動時に-Xnoclassgcを使って、パーマネント領域全体をGCの対象外とする
  • Singletonクラス群の参照を保持し続けるための専用のクラスを作る

GCの恩恵を他の場所で受けられるからという理由で、2つ目の方法が推奨されるようです。

恥をさらす余談

本文のサンプルコードを淡々と写経しつつ、練習問題にも一応取り組んでいるわけですが…。

インスタンスの個数が3個に限定されるクラスを作る。インスタンスには0、1、2の番号がついていて、getInstance(int id)で、id番のインスタンスを得られる」という問題5-2。

結城さんの回答は、クライアントのコードで整数を3で割り、余りをgetInstanceの引数として渡している。つまり、必ず引数が0〜2になるように、クライアント側で調整している。それに比べて、自分が書いたのは、0〜2以外の引数でgetInstanceを使おうとしたらIllegalArgumentExceptionを投げるという酷い仕様w こういう些細なところに、習慣の良し悪し、発想の柔軟性や人としての親切さが出るような気がする…だめだめです。

たしか、結城さんの本『プログラマの数学』で、「除算の余りを上手く使えるようになりましょう」という話を読んだハズなのになぁ…。理解することと自然に使えることの間には、開きがありますね。

Connection: close