同時-volatile(二)
6588 ワード
問題は最高の指導者で、細心の注意は最高の品質です.
これは安全な怠け者式の単例実現であり、古典的な設計モデルである.
前の文章では
Javaメモリモデルでは3つの特性が規定されています原子性は、付与操作 のようないくつかの操作が原子性を有することを規定する.秩序性、実行順序の最適化による実行効率の向上 の可視性は、1つのスレッドが共有変数の値を変更すると、他のスレッドが直ちにこの変更を知ることができるかどうかである.
以下はJavaメモリモデルと命令の並べ替えから参照します. Happen-Before先行発生規則 順序原則1つのスレッド内で意味のシリアル性を保証する.a = 1; b = a + 1; .のロック規則解除( 伝達性AはBより先に、BはCより先に、Aは必然的にC. より先になる.スレッドが起動する、中断する、スレッドを終了する スレッドの割り込み( オブジェクトの終端オブジェクトのコンストラクタ実行が
3行のコードがあります
通常の実行方法は順番に実行されますが、1行目と2行目には前後依存関係がないことがわかります.1行目と2行目を同時に実行すれば、実行速度を向上させることができますか?
そこで
一部の商用仮想マシンでは、Javaプログラムは最初に解釈器(
リファレンスhttps://www.cnblogs.com/linghu-java/p/8589843.html
したがって、上記コードの実行時には、第1行と第2行が同時に実行する、第3行が実行されるように最適化することができる.
多くの場合の命令再配置は運転効率を速めるが、いくつかの特殊な場合の命令再配置はいくつかの位置決めが困難な問題をもたらす可能性があり、そのうちインスタンス変数 を付与する.メモリ領域 は、スタック内でオブジェクトフィールドには である.は構造方法を呼び出し、初期値 とする.スタック内参照をスタック内オブジェクトと接続する オブジェクト作成完了 並べ替えが許可されている場合、つまりスタック内参照をスタック内オブジェクトと接続する は構造方法を呼び出し、初期値 とする.
接続を確立する後、他のスレッドがこのとき第1の重みの
説明命令再配列が発生する確率は非常に小さく、 が発生する可能性があるのは一定のレベルの同時性が必要である.
これはロックされたコードブロックは、第1の再判定と第2の再判定との間で、すべてのスレッドが第1の再判定 に入る. .
メモリバリアは命令の間の高い壁であり、越えてはいけないことを理解しています.読み書きだけが順番に分かれていて、それぞれ以下の命令です にデータを読み出す. にデータを書き込む.
それに対応して4種類のメモリバリアがあります
オペレーティングシステムレベルの実装命令を拡張
詳細はメモリバリアの詳細を参照してください
コードを貼り付けて、ソースMemory Reordering Caught in the Actを
実行回数が十分に多い場合にはループが終了する、このときコマンドの並び替えが発生したことを示す.
DCL
は必ずvolatile
を追加しなければなりませんか?一、DCL
DCL
全称double check lock
、二重チェックロックこれは単例モードの実現であり、現在の標準装備実現でもあり、その前に
/
などの実現があり、まずDCL
の実現を見てみましょうpublic class DclSingleBean {
private volatile static DclSingleBean instance;
private DclSingleBean() {
System.out.println("doSomeThing...");
}
public static DclSingleBean getInstance() {
if (null == instance) {
synchronized (DclSingleBean.class) {
if (null == instance) {
instance = new DclSingleBean();
}
}
}
return instance;
}
}
これは安全な怠け者式の単例実現であり、古典的な設計モデルである.
volatile
キーワードここでの意味は何ですか?volatile
は削除できますか?二、volatileについて
前の文章では
volatile
の可視性について話しましたが、もう一つのまぶしい特性があります.
と呼ばれています.0.JMM-Javaメモリモデル
Javaメモリモデルでは3つの特性が規定されています
i = 1
以下はJavaメモリモデルと命令の並べ替えから参照します.
sychronized
とvolatile
だけでプログラム実行中の原子性、秩序性、可視性を保証すると、コードは異常に煩雑になる.JMM
は、Happen-Before
のルールを提供し、データ間の競合の有無、スレッド環境の安全性を制約します.具体的には、次のとおりです.volatile
ルールvolatile
変数の書き込みは、まず読み取りに発生し、これはvolatile
変数の可視性を保証し、unlock
)は、後続のロック解除(lock
)の前に必ず発生する.start()
方法は、その各動作よりも先に行われる.interrupt()
)は、割り込むスレッドのコードよりも先である.スレッドのすべての動作は、スレッドの終了よりも先に(Thread.join()
).finalize()
より先に終了する方法.1.命令再配置とは
3行のコードがあります
1 int a = 1;
2 int b = 2;
3 int c= a + b;
通常の実行方法は順番に実行されますが、1行目と2行目には前後依存関係がないことがわかります.1行目と2行目を同時に実行すれば、実行速度を向上させることができますか?
そこで
JVM
の建設者は同様の最適化を行い、この最適化は
であり、その名の通り命令の実行順序を一定の規則に従って並べ替え、より速く動作させる.JVM
でこのことをしたのはJIT
<Just In Time Compiler
>インスタントコンパイラです一部の商用仮想マシンでは、Javaプログラムは最初に解釈器(
Interpreter
)によって解釈実行され、仮想マシンがある方法やコードブロックの実行が特に頻繁であることを発見すると、これらのコードは
と認定される.ホットスポットコードの実行効率を向上させるために、実行時にインスタントコンパイラ(Just In Time Compiler
)は、これらのコードをローカルプラットフォームに関連するマシンコードにコンパイルし、様々な階層の最適化を行う.リファレンスhttps://www.cnblogs.com/linghu-java/p/8589843.html
したがって、上記コードの実行時には、第1行と第2行が同時に実行する、第3行が実行されるように最適化することができる.
2.指令再配置による問題
多くの場合の命令再配置は運転効率を速めるが、いくつかの特殊な場合の命令再配置はいくつかの位置決めが困難な問題をもたらす可能性があり、そのうち
DCL
は比較的典型的な1つである.DclSingleBean
クラスのインスタンス化プロセスの解析を容易にするために、フィールドを追加します.public class DclSingleBean {
private int x;
public int getX() {
return x;
}
private volatile static DclSingleBean instance;
private DclSingleBean() {
x = 8;
System.out.println("doSomeThing...");
}
public static DclSingleBean getInstance() {
if (null == instance) {
synchronized (DclSingleBean.class) {
if (null == instance) {
instance = new DclSingleBean();
}
}
}
return instance;
}
}
x
を増加する、構築時に初期値DclSingleBean
のクラスのインスタンス化プロセスは、以下のステップに簡略化することができる.private volatile static DclSingleBean instance;
private DclSingleBean() {
x = 8;
}
instance = new DclSingleBean();
instance
に割り当てる.
の値が付与、ここでのx
はint
のタイプであるため、このときx
は0
x
を8
volatile
のキーワードを削除し、第3ステップ構造と第4ステップ確立変換の実行順序が入れ替わるとしたらどうなりますか?x
を8
接続を確立する後、他のスレッドがこのとき第1の重みの
null == instance
に入ると判断し、false
が得られたのは、接続が確立するからである.そしてこの
の対象instance
に直接戻る、そのうちのx
の値が0
となると、ここでの問題が露呈する.説明
これは
volatile
の禁止命令の並べ替えの経典の例で、よく考えてみることができて、陥りやすい誤区は私がロックをかけた以上、どうして他のスレッドが現在のスレッドを手に入れることができてまだ実例の完全な対象がありませんか?static
修飾変数はグローバルに可視であり、接続が確立されるとnull
ではないに違いない3.指令再配置禁止-メモリバリア
メモリバリアは命令の間の高い壁であり、越えてはいけないことを理解しています.読み書きだけが順番に分かれていて、それぞれ以下の命令です
load
ホストメモリからワークメモリstore
ワークメモリからプライマリメモリそれに対応して4種類のメモリバリアがあります
StoreStoreBarrier
LoadLoadBarrier
StoreLoadBarrier
LoadStoreBarrier
StoreStoreBarrier
は書き込みと書き込みの間のメモリバリアであり、バリアの前のstore
の動作がバリアの後のstore
に発生することを保証する、他の3つは類似している.StoreLoad Barriers
は他の3つのバリアの効果を同時に備えているため、全能バリア(mfence
)とも呼ばれ、現在多くのプロセッサでサポートされている.しかし、他のバリアに比べて、バリアのオーバーヘッドは比較的高価である.オペレーティングシステムレベルの実装命令を拡張
Windows
lock addl
Linux
sfence
lfence
mfence
詳細はメモリバリアの詳細を参照してください
4.命令再配置の例
コードを貼り付けて、ソースMemory Reordering Caught in the Actを
Java
言語で実現した.public class CatchReOrderClass {
private static int a, b, x, y;
public static void main(String[] args){
int i = 0;
for (;;) {
a = 0; b = 0;
x = 0; y = 0;
new Thread(() -> {
a = 1;
x = b;
}).start();
new Thread(() -> {
b = 1;
y = a;
}).start();
i++;
if (x == 0 && y == 0) {
System.out.println(i);
break;
}
}
}
}
実行回数が十分に多い場合にはループが終了する、このときコマンドの並び替えが発生したことを示す.