yohhoyの日記

技術的メモをしていきたい日記

Immutableクラスとfinalフィールドの関係

プログラミング言語Javaにて Immutable なクラスを実装する場合、同フィールドのfinal修飾が必要な理由についてメモ。

Immutable オブジェクトを用いる利点の一つとして、自動的*1に保証されるスレッド安全性(thread-safety)が挙げられる。このような Immutable クラスの実装では、不変値を保持するフィールドをfinal修飾する必要があり、これは2つの重要な意味をもつ。本記事では後者の観点を扱う。

  • コンストラクタ以外では値変更を許容しないことを表明(不変性)
  • Immutable オブジェクトのスレッド安全性を保証する(“initialization safety”*2

下記例ではフィールドxをfinal修飾していないため、メソッドgetX内のフィールドxアクセスはスレッド安全ではない*3。このため、同インスタンスを生成したスレッドAとは異なるスレッドBからは、未初期化のフィールドx値(この例ではnull)を意図せずReadする可能性がある。言い換えると、スレッドBからはインスタンス構築途中の状態を見てしまう危険性がある。

public class final C {
  private String x;  // final修飾なし

  public C(String x) {
    this.x = x;
  }
  public String getX() {
    return x;  // NG: 初期化処理とデータ競合しうる
  }
}

final修飾されたフィールドの初期化であれば、コンストラクタの終了時点で他スレッドから可視になることが保証される。Java Language Specification 3rd Ed.(Java SE 5.0)より一部引用。

Final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. Final fields must be used correctly to provide a guarantee of immutability.

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

The usage model for final fields is a simple one. Set the final fields for an object in that object's constructor. Do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.

CHAPTER 17 Threads and Locks, 17.5 Final Field Semantics

より形式的には、同JLS §17.5.1 Semantics of Final Fieldsにてfreeze actionにより定義されている。

関連URL

*1:ここでの “自動的” とは「synchronized等を用いた明示的な排他制御コードを記述しなくても、getter メソッドでのフィールドReadについてスレッド安全性が得られる」ことを指す。

*2:JSR 133 (Java Memory Model) FAQより:"The goals of JSR 133 include: A new guarantee of initialization safety should be provided. If an object is properly constructed (which means that references to it do not escape during construction), then all threads which see a reference to that object will also see the values for its final fields that were set in the constructor, without the need for synchronization."

*3:メモ:この文脈での “スレッド安全でない” とは、「Immutable オブジェクトに期待されるスレッド安全性を満たさない」というニュアンスが正確か。なんらかのスレッド間同期機構を利用しコンストラクタ完了と getX メソッド呼出の間に happens-before 関係が成り立つなら、データ競合は生じずスレッド安全であると言える。Java言語イディオムとしての Immutable オブジェクトでは、このような同期機構なしにアクセスしてもデータ競合を引き起こさないことが求めらる。(というより、暗に期待されていたスレッド安全性を満たすため JSR-133 でJavaメモリモデルが修正されたというのが実情。)