揮発性修飾子の基本特性
volatile修飾子が適用された共有変数(クラスメンバ変数、静的メンバ変数)は次の特性を獲得します:
- 異なるスレッド間での可視性の保証(変数変更が即時に他スレッドに反映)
- 命令の再順序付けの禁止
- 原子性(atomicity)は保証しない
※ synchronizedとLockは可視性・順序性・原子性の全てを保証
メモリ可視性のメカニズム
Javaメモリモデルにおける動作原理:
// スレッド1
int temp = sharedValue; // 主記憶から読み取り
temp = temp + 10; // 作業メモリで変更
sharedValue = temp; // 主記憶へ書き戻し
// スレッド2
int localCopy = sharedValue; // 変更前の古い値を取得
volatile変数は:
- 書き込み時に直ちに主記憶を更新
- 他スレッドのキャッシュを無効化
命令再順序付けの制御
典型的なダブルチェックロッキングパターン:
private volatile static Singleton uniqueInstance;
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
volatileなしの場合:
- メモリ割当
- 参照代入(未初期化状態)
- コンストラクタ実行
volatileによりコンストラクタ完了まで参照代入をブロック
有効な適用ケース
状態フラグ管理
volatile boolean isReady = false;
// 初期化スレッド
configuration = loadSettings();
isReady = true;
// 処理スレッド
while (!isReady) {
Thread.yield();
}
processConfiguration(configuration);
原子性操作の限界
volatileが保証しない操作例:
volatile int counter = 0;
// スレッド安全ではない
public void increment() {
counter++; // 読み取り→変更→書き込みの非原子操作
}
基本型の直接代入のみ原子性を保証:
volatile int flag = 1; // 安全
volatile DataPacket packet = new DataPacket(); // 参照代入は原子操作
同期メカニズムの比較
| 特性 | volatile | synchronized |
|---|---|---|
| 可視性 | ○ | ○ |
| 順序性 | ○ | ○ |
| 原子性 | × | ○ |
| 競合防止 | × | ○ |