« データとオブジェクト | トップページ | プログラマ不要論 »

2007年2月25日 (日)

動的定数のテクニック

Javaで定数としてよく使われるのが、
 public static final String xxx = "abcde";
というStringフィールドである。

しかし、この定数は最初から値が決まってしまっているので、
定数の設定をコーディング時に決めなければいけないという欠点がある。

たとえば、どんな値を入れるかわからないが、最初に格納された値を定数として保持したい、という場合があるだろう。
この場合、最初に格納される値が何なのか不明であり、したがってfinal属性でコーディングすることができない。
しかしfinal属性にしないと定数として保護されないという問題がある。

そこでどうするかというと、最初に格納されるオブジェクト以外は弾いてしまうようなフィルターを設ける。ただしフィールドは privateにして、getterとsetterを設ける。

例:
private static String sss = null;

// sss が nullの場合に値を入れる
// マルチスレッド下でのフィールド保護のため同期化
public static synchronized void set_sss(String xxx){
  if(sss == null) {
   sss = xxx;
  }
}

// sss が nullでない場合に値を返す
// getterでは同期化は不要
public static String get_sss(){
  if(sss != null){
   return sss;
  } else {
   return null;
  }
}

これで最初に格納される値を定数にすることができるようになる。
これは、最初に格納される値を定数で持ちたい、というような「動的な条件の定数」を実現するテクニックである。このような定数はfinal属性で最初から指定することができないために有効なテクニックとなる。

final属性の定数とは違って、定数の内容が動的に定義されることから、動的定数と勝手に呼んでいる。

マルチスレッド下では、フィールドの保護のために setterを synchronizedメソッドにする必要があるだろう。

尚、フィールドに volatileをつければフィールドの可視性が最新となるから、synchronizedメソッドは必要ないのでは、と思うかもしれない。
しかしこれは違うのである。
確かに volatileによってフィールドの可視性は最新が保証されるが、これはフィールド代入操作がアトミック(排他的)になることを保証しないのである。
というのは、ある時点でフィールドが nullだと判断したスレッドが複数いた場合(これはあり得る)、代入が2回以上行われてしまうからである。
つまり以下のようなことが起こりうる。
 1.スレッドAがフィールドをnullだと認識
 2.スレッドBがフィールドをnullだと認識
 3.スレッドAがフィールドに値を代入
 4.スレッドBがフィールドに値を代入

これで最初の狙いであった、最初に代入された値を定数として保持する、という狙いは失敗しているのである。
代入の排他性を実現するためには、やはり synchronizedメソッドにするしかない。
尚、Java5.0からは新規に追加された concurrentパッケージのクラスを使用することでsynchronizedを使用せずに排他制御が可能になったようである。

|

« データとオブジェクト | トップページ | プログラマ不要論 »

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: 動的定数のテクニック:

« データとオブジェクト | トップページ | プログラマ不要論 »