揮発性の紹介
揮発性の概要
- volatileは
比synchronized
キーワード更轻量级
で同步机制
あり、volatile変数にアクセスする不
と加锁
操作が実行されるため、不
実行され线程阻塞
ます。 - 揮発性の保証
可见性
と禁止指令重排序
、底部を通って「です内存屏障
達成するために」、しかし不保证原子性
。 写入
揮発性の変数は以下と等価である退出同步代码块
、读取
揮発性変数と同等进入同步代码块
。
揮発性の使用シナリオ
- 変数の
写
入力操作により、変数の値を不依赖变量的当前值
確認でき只有单个线程更新
ます。 - 変数
不会与其他状态变量一起纳入不变性条件中
;(変数は他の変数の不変条件には含まれません) - 変数にアクセスするとき
不需要加锁
。
ユースケース
状态标记量
:ステータスフラグに従って、スレッドを終了します。つまり、特定のステータスフラグをチェックしてループを終了するかどうかを決定するシナリオでは、volatileを使用できます。单例模式中的double check
:instance = new Singleton()
このコード行はアトミック操作ではないため、インスタンスを揮発的に変更し、インスタンスにメモリを割り当て、シングルトンのコンストラクターを呼び出してメンバー変数を初期化し、インスタンスオブジェクトを割り当てられたメモリ空間にポイントします。
揮発性変数の定義に関するJMMの特別なルール:
- 他のスレッドによる変数の変更を確実に確認できるように、現在のスレッドを毎回使用する
使用变量前都
必要があります先从主内存刷新最新的值
。(read、load、use连续执行
、主
メモリからメモリへ工作
。) - 現在
修改变量后
、立刻同步
他のスレッドがスレッドへの変更を確認できるように、毎回メインメモリにアクセスする必要があります。(メモリassign、store、write
から工作
メモリへの連続実行主
。) - volatileによって変更された変数
不会被指令重排序优化
は、コードの実行順序がプログラムの順序と同じであることを保証します。
命令の順序変更の最適化を禁止する可視性と原則
(基になるロック命令によって実現)
volatileはスレッドの可視性を保証し、特定の順序を提供できますが、原子性は保証できません。volatileはJVMの下部にある「内存屏障
」で実装されます。セマンティクスの2つの層:保证可见性,不保证原子性
; 禁止指令重排序
。
可視性
すべてのスレッドに対して 揮発性変数の可視性を確保します。可視性とは、スレッドがこの変数の値を変更すると、新しい値が他のスレッドにすぐに認識されることを意味します。通常の変数は許可されていません。スレッド間の共通変数の値の転送は、メインメモリを通じて行う必要があります。たとえば、スレッドAは通常の変数の値を変更してメインメモリに書き戻します。別のスレッドBは、スレッドAが書き込みを完了した後に書き戻しますメインメモリから読み取った後にのみ、新しい変数値がスレッドBに表示されます。
揮発性の基盤となることにより、ロックの接頭辞、そう効果という存在キャッシュCPUのメモリに書き込まれ、書き込み動作が発生します別のCPUまたはその他のコアのキャッシュの無効化、キャッシュJMM変数と同等であった「ストアと書き込みを「操作、このスレッドは他のスレッドの変数のキャッシュラインを無効にします。他のスレッドが変数を読み取り、変数のキャッシュラインが無効であることを検出する必要がある場合、データはメインメモリから再ロードされるため、データの可視性が保証されます。揮発性変数を
書き込むことは、同期コードブロックを終了することと同等であり、揮発性変数を読み取ることは、同期コードブロックを入力することと同等です。ロック機構は可視性と原子性の両方を保証できますが、揮発性変数は可視性のみを保証し、原子性は保証しません。
命令の並べ替えの最適化を禁止する
変数がvolatileとして宣言されると、コンパイラーとランタイムは変数がsharedであることを認識します。これは、変数に対する操作が他のメモリー操作と並べ替えられず、volatile変数がレジスターまたはその他にキャッシュされないためです。プロセッサーが見えない場所。
volatileによって変更された変数は、アセンブリコードで追加のロック操作を実行します。これは、メモリバリア(メモリバリア、つまり、並べ替え時に後続の命令をメモリバリアの前の位置に並べ替えることができない)と同等です。
1つのCPUのみがメモリにアクセスする場合、メモリバリアは必要ありませんが、複数のCPUが同じメモリにアクセスし、そのうちの1つが他のCPUを監視している場合、一貫性を確保するためにメモリバリアが必要です。ロック命令がメモリへの変更を同期化する場合、それは以前のすべての操作が実行されたことを意味し、命令の並べ替えがメモリバリアを越えることができないという感覚を形成する可能性があります。
命令の順序変更を禁止する揮発性の原則:
- とき
第二个操作是volatile写
の時間は、何の最初の操作が何であるかは関係ありません都不能重排序
。このルールにより、揮発性書き込み前の操作が、揮発性書き込み後にコンパイラによって並べ替えられなくなります。 - とき
第一个操作是volatile读
の時間は、何の2番目のアクションが何であるかは関係ありません都不能重排序
。このルールは、揮発性読み取り後の操作が、揮発性読み取り前にコンパイラーによって再順序付けされないようにします。 - ときは
第一个操作是volatile写
、第二个操作是volatile读
する場合、不能重排序
。
質疑応答
as-if-serialセマンティクスが制御の依存関係を持つ操作の並べ替えを許可する理由は何ですか?
シングルスレッドプログラムでは、制御依存関係のある操作の順序を変更しても実行結果は変わりませんが、マルチスレッドプログラムでは、制御依存関係のある操作の順序を変更してもプログラムの実行結果が変わる可能性があります。
発生前の原則は何ですか?
- コードルールの順次実行:同じスレッド内で、前の操作が後続の操作の前に発生します。
- ロック解除ルールを追加:モニターでの
解锁
操作は、その後の加锁
操作の前に行われます。 - 揮発性ルール:
volatile
変数の写
操作は後続の读
操作の前に発生します。 - スレッド起動ルール:スレッド
start()
メソッドは、スレッドの後続のすべての操作の前に発生します。 - スレッドには、ルールを終了:すべての操作が起こる-前に、そのスレッドに別のスレッドの呼び出しスレッド
join()
操作の成功後に返します。 - スレッド割り込み規則:スレッド
interrupt()
メソッドの呼び出しは、割り込みコードが割り込みイベントの発生を検出する前に発生し、Thread.interrupted()
メソッドは割り込みが発生したかどうかを検出できます。 - オブジェクト終了規則:
对象的初始化完成
発生前のfinalize()
メソッドの開始(コンストラクターの実行の終了)。 - 推移性:bの前に発生する場合、cの前に発生する場合、cの前に発生する場合。
ロックと揮発性の違いは何ですか?
類似点:揮発性変数の
書き込みは同期コードブロックの終了と同等であり、揮発性変数の読み取りは同期コードブロックの入力と同等です。
相違点:
ロックメカニズムは可視性と原子性の両方を保証できますが、揮発性変数は可視性のみを保証し、原子性は保証しません。
通常の変数と揮発性変数の違いは何ですか?
volatile
特別なルール保证新值能立即同步
に主内存
、各使用前
すぐにできます从主内存刷新
。したがって、volatileは多线程
操作可见性
中に変数を保証できます普通变量不保证
。
- 揮発性の変更された変数は「可視性」を保証できます。スレッドがこの変数の値を変更すると、新しい値はすぐに他のスレッドに認識されます。
- 通常の変数は「可視性」を保証できません。スレッド間で共通変数の値を転送するには、メインメモリを完了する必要があります。スレッドAは共通変数の値を変更し、最初にロックしてメインメモリから読み取り、次にメインメモリに書き戻します(ロック、読み取り、読み込み、使用、割り当て、保存、書き込み、ロック解除)。スレッドAのライトバックが完了(ロック解除)された後、別のスレッドBがメインメモリから最新の値(ロック、読み取り、ロード、使用、ロック解除)を読み取り、新しい変数値がスレッドBに表示されます。
原子性の意味論的意味は何ですか?
volatile不保证原子性
、しかし、アトミックセマンティクスについて話します。
:1)原子に定義される
操作または操作の、すなわち複数要么全部执行
と実行过程不会
任意の因子によって打断
、要么都不执行
。
2)例:
i = 2;
i = j ;
i++;
i = j + 2;
Javaでは、変数と割り当て動作の基本的なデータタイプがアトミック操作であることを保証するために(それが32ビットJDK環境にある場合の動作、ハイとローとに分割される。)アトミック手順全体として、単一スレッドのおそれセクシュアリティですが、マルチスレッドでは、同期とロックによって原子性を確保する必要があります。この操作は、基本データタイプ2をiに割り当てることのみに関与します; i = j、yes 、最初にjの値を取り、次にjの値をiに割り当てます; i ++、yes 、最初にiの値を取り、次にiの値をインクリメントします、最後に、増分した結果をi、yesに割り当てます; i = j + 2、yes 、最初にjの値を取り、次にj + 2を取り、最後にj + 2の結果をi、yesに割り当てます。long和double
64位数据
读取不是原子
两次32位操作
i = 2
原子的
两个操作
非原子操作
三个操作
非原子操作
三个操作
非原子操作
JDK1.5以前は、JavaがDCL(ダブルロック検出)を安全に使用してシングルトンモードを実装できないのはなぜですか?
揮発性マスキング命令の並べ替えのセマンティクスは、JDK1.5では完全に修正されていませんでした。
1)DCLシングルトンモード:
public class Singleton {
// volatile修饰变量
private volatile static Singleton instance;
private Singleton(){}
// 单例方法
public static Singleton getInstance(){
if(instance == null){
// 加锁synchronized代码块
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton.getInstance();
}
}
2)静的内部クラスを使用してより安全なメカニズムを実装する
/* 静态内部类 */
public class SingletonInner {
// 静态内部类
private static class Holder {
private static SingletonInner singleton = new SingletonInner();
}
private SingletonInner(){}
public static SingletonInner getSingleton(){
return Holder.singleton;
}
}