目次
1.揮発性
同時プログラミングでは、複数のスレッドが共有変数を操作すると、データの競合や可視性の問題など、スレッドの安全性の問題が発生する可能性があります。これらの問題を解決するために、Java は JUC (java.util.concurrent) ツールキットを提供します。このツールキットには、同時プログラミングを処理するための多くのツール クラスとインターフェイスが含まれています。JUC では、volatile
これは変数を変更して変数の可視性を確保し、命令の並べ替えを禁止するために使用できるキーワードであり、スレッド セーフティの問題をある程度解決します。
1.1 volatile
キーワードの役割
1.1.1 変数の可視性
マルチスレッド環境では、スレッドが共有変数の値を変更すると、スレッド間のデータの不一致により、他のスレッドが変数の最新の値を認識できない可能性があります。この種の問題は「変数不可視性」または「可視性問題」と呼ばれます。
volatile
キーワードを使用して、修飾された変数がすべてのスレッドに確実に表示されるようにします。スレッドがvolatile
変数の値を変更すると、他のスレッドはキャッシュ内の古い値を使用せずに、最新の変更値をすぐに確認できます。
1.1.2 命令の並べ替えを無効にする
JVM (Java Virtual Machine) では、パフォーマンスを最適化するために、コンパイラーとプロセッサーが命令の順序を変更する場合があります。シングルスレッド環境では、この並べ替えはプログラムの実行結果には影響しません。ただし、マルチスレッド環境では、命令の並べ替えによりスレッドの安全性の問題が発生する可能性があります。
volatile
キーワードを使用すると、命令の並べ替えを防止し、変更された変数がコード内の順序で確実に実行されるようにすることができます。
1.2 可視性が不安定なケース
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
new Thread(() -> {
while (!flag) {
System.out.println("Waiting for the flag to be true...");
}
System.out.println("Flag is now true. Exiting the thread.");
}).start();
try {
Thread.sleep(1000); // 确保主线程在子线程之前执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Setting the flag to true...");
flag = true;
}
}
上の例では、VolatileExample
クラスを作成し、volatile
型の変数を宣言しましたflag
。メインスレッドでは、新しいサブスレッドを開始します。変数の値が変更され、サブスレッドが終了するflag
まで、変数の値が常にチェックされます。flag
true
メインスレッドでは、flag
変数を に設定しますtrue
。flag
変数は型として宣言されているためvolatile
、子スレッドはflag
時間内の最新の値を確認できるため、ループを終了して「フラグが true になりました。スレッドを終了します。」を出力します。
この例では、変数の可視性volatile
を確保するためのキーワードの役割を示しますflag
。このキーワードを使用しなかった場合volatile
、子スレッドはメイン スレッドのflag
変更を確認できないため、永久にループしていた可能性があります。
1.3 揮発性の非アトミックなケース
public class Test {
public static void main(String[] args) throws InterruptedException {
VolatileAtomicityExample example = new VolatileAtomicityExample();
for(int i=1;i<=100;i++){
new Thread(()->{
for(int j=1;j<=1000;j++)
example.increment();
},String.valueOf(i)).start();
}
TimeUnit.SECONDS.sleep(2);
System.out.println(example.getCount());
}
}
class VolatileAtomicityExample {
volatile int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
型として宣言されたVolatileAtomicityExample
メンバー変数を持つクラスを作成します。次に、100 個のスレッドを作成し、各スレッドがそれぞれ 1000 回の操作を実行し、自己インクリメントしました。最後に、メインスレッドで最終値を出力します。上記の例の出力は、実行時の不確実性により異なる場合があります。実行するたびに異なる結果が得られる場合がありますが、通常、結果は 100000 未満です。この問題を解決するには、キーワードまたはその他のスレッド セーフティ メカニズムを使用して、メソッドのアトミック性を確保する必要があります。count
volatile
increment()
count
count
synchronized
increment()
理由は何ですか?
揮発性変数に可視性を持たせる ために、JVM はメイン メモリからスレッドの作業メモリにロードされた値が最新であることのみを保証し、データがロードされるときにのみ最新になります。ただし、マルチスレッド環境では、「データの計算」と「データの代入」の操作が複数回発生する可能性があり、データのロードやメインメモリの volatile 修飾子変数の変更が行われると、スレッドのワーキングメモリでの操作が行われなくなります。メインメモリの最新値の読み取りは無効になり、操作には書き込み損失の問題があります。つまり、各スレッドのプライベートメモリとメインメモリのパブリックメモリの変数が同期せず、データの不整合が発生します。volatile は変数の読み取り時の可視性の問題を解決しますが、アトミック性は保証できません。複数のスレッドがメイン メモリ内の共有変数を変更するシナリオでは、ロック同期を使用する必要があります。
1.4 volatile は並べ替えを禁止します
メモリ バリアは、マルチスレッド環境でメモリの可視性と正しい実行順序を確保するためにメモリ操作の順序を制御するために使用されるハードウェア命令またはコンパイラ命令です。
メモリ バリアには 2 つのタイプがあります。
-
メモリ読み取りバリア (ロード バリア): これは、メモリ読み取り操作の前に、以前のすべての書き込み操作が完了し、その結果が現在のスレッドに表示されることを確認するために使用される特別なハードウェアまたはコンパイラ命令です。つまり、読み取りバリアは、後続の読み取り命令が読み取りバリアの前の位置に並べ替えられるのを防ぎます。
-
メモリ書き込みバリア (ストア バリア): これは、先行するすべての書き込み操作と書き込みバリア前の書き込み操作がメモリ書き込み操作の前に完了していることを保証するために使用される特別なハードウェアまたはコンパイラ命令であり、その結果は他の影響を受けません。スレが見られます。つまり、書き込みバリアは、前の書き込み命令が書き込みバリアの後の位置に並べ替えられるのを防ぎます。
volatile
このキーワードにより、変数の読み取りおよび書き込み操作がメモリ バリアを介して順序変更されないことが保証されます。具体的には、volatile
変数の書き込みの場合、変数の書き込み後に書き込みバリアが挿入され、書き込みバリアの前に他の命令が並べ替えられるのを防ぎます。同様に、volatile
変数の読み取り操作の場合、変数が読み取られる前に読み取りバリアが挿入されます。これにより、他の命令が読み取りバリアの前に並べ替えられるのが防止されます。
このように、volatile
キーワードにより、変数の読み取りおよび書き込み操作に特定の順序が与えられるようになり、マルチスレッド環境でのメモリの可視性と正しい実行順序が確保されます。
1.5 不安定な毎日の使用シナリオ
ステータス フラグ: スレッドが特定のステータス フラグを変更すると、他のスレッドは最新のステータスを即座に確認する必要があります。現時点では、キーワードを使用してvolatile
ステータス フラグを変更し、複数のスレッド間でステータス フラグを確実に認識できるようにすることができます。例えば:
public class Task implements Runnable {
private volatile boolean isRunning = true;
@Override
public void run() {
while (isRunning) {
// 执行任务逻辑
}
}
public void stop() {
isRunning = false;
}
}
二重チェック ロック (二重チェック ロック): マルチスレッド環境で、オブジェクトを遅延初期化する必要がある場合、初期化の繰り返しを避けるために、二重チェック ロックがよく使用されます。この場合、volatile
マルチスレッド環境でオブジェクトの可視性を確保するにはキーワードが必要です。例えば:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
上記のコードでは、単純なシングルトン パターンを実装しました。このメソッドではgetInstance()
、二重チェックされたロックを使用して遅延初期化を実装し、instance
新しいSingleton
インスタンスが空の場合にのみ作成されるようにします。
ただし、マルチスレッド環境では、命令の並べ替えが存在するため、次の問題が発生する可能性があります。
-
オブジェクト参照は null ではありませんが、まだ初期化されていません: スレッド A が
instance = new Singleton();
この行の実行を完了する前に、命令の並べ替えが発生してinstance
null 以外の参照が生成される可能性がありますが、Singleton
インスタンスの初期化はまだ完了していません。このようにして、スレッド B はreturn instance;
実行時に初期化されていないオブジェクトを取得し、エラーが発生します。 -
可視性の問題: 命令の並べ替えにより、スレッド B がスレッド A の初期化操作を時間内に認識できなくなる可能性もあります。たとえば、スレッド A
instance
のペアへの割り当ては、スレッド A の背後で実行されるように並べ替えられるため、スレッド B はinstance
読み取り時に古い参照を取得し、スレッド A の初期化操作を認識しません。
この問題を解決するには、Singleton
インスタンスの作成時にキーワードを使用してvolatile
オブジェクトの可視性を確保し、命令の並べ替えを禁止する必要があります。
本の配達
「シリコンベースの物語 わたしはソウルペインター」
AIと絵画が出会うとき、どんな素晴らしい世界が広がるのでしょうか?
ChatGPT+Midjourney を使用して人間の魂と夢を探求する
StableDiffusion+D-IDで若々しく華やかな欲望を引き出す
みんなの隠れた絵の才能を開花させる
誰でもトプ画マスターになれる
ChatGPT+ミッドジャーンシー+安定拡散+D-ID+RunwayGen-l
爆発的なソフトウェアのフルプロセスコラボレーション
手のひらデータAI描画ビジネススキル
ウルトラフルペイントキー部門のロックを解除します
創造の基礎となるロジックを説明する
AI ペイントの全プロセスを説明した本
簡単な紹介
AI ペイントの秘密を探るためのガイドで、豊富な実践事例を通じて AI ペイントの生成手順をわかりやすく解説し、AI ペイントの不思議な魅力を鮮やかに示します。歴史から未来まで、1世紀の時空を超え、理論から実践まで、テクノロジーから哲学、多次元、言語から絵画、そして実践的な演習まで、ケース・オペレーションについて語ります。AI ペイントの誕生は特異点の到来を引き起こし、AGI (汎用人工知能) に光を当て、プロンプト、スタイル、技術的詳細、マルチモーダル インタラクション、AIGC などの一連の詳細な説明が必要になりました。描画スキルを簡単にマスターし、ユニークな芸術作品を作成し、独自の芸術時代を書きましょう。
ダンダンリンク: http://product.dangdang.com/29601870.html
ブロガーをフォローしたり、「いいね!」したり、お気に入りにしたり、
コメント欄のコメント
本のプレゼント企画に参加しよう!