volatile
今日の仕事で、Javaのキーワードに出くわしました。よくわからないので、この説明を見つけました。
その記事が問題のキーワードを詳細に説明していることを考えると、あなたはそれを使用したことがありますか、またはこのキーワードを正しい方法で使用できるケースを見たことがありますか?
volatile
今日の仕事で、Javaのキーワードに出くわしました。よくわからないので、この説明を見つけました。
その記事が問題のキーワードを詳細に説明していることを考えると、あなたはそれを使用したことがありますか、またはこのキーワードを正しい方法で使用できるケースを見たことがありますか?
volatile
メモリの可視性のためのセマンティクスがあります。基本的に、volatile
書き込み操作が完了すると、フィールドの値はすべてのリーダー (特に他のスレッド) に表示されます。がないvolatile
と、読者は更新されていない値を見ることができます。
あなたの質問に答えるには: はい、volatile
変数を使用して、一部のコードがループを継続するかどうかを制御します。ループはvolatile
値をテストし、そうであれば継続しますtrue
。false
条件は、「停止」メソッドを呼び出すことで設定できます。ループはfalse
、stop メソッドの実行が完了した後に値をテストするときに、確認して終了します。
私が強くお勧めする本「Java Concurrency in Practice」は、.NET について適切に説明していvolatile
ます。この本は、質問で参照されている IBM の記事を書いたのと同じ人物によって書かれています (実際、彼はその記事の最後に彼の本を引用しています)。私が使用するのvolatile
は、彼の記事で「パターン 1 ステータス フラグ」と呼ばれるものです。
が内部でどのように機能するかについて詳しく知りたい場合は、 Java メモリ モデルvolatile
を読んでください。そのレベルを超えたい場合は、Hennessy & Pattersonなどの優れたコンピューター アーキテクチャの本を調べて、キャッシュの一貫性とキャッシュの一貫性について読んでください。
volatile
スレッドを停止するのに非常に便利です。
独自のスレッドを作成する必要があるわけではありませんが、Java 1.6 には多くの優れたスレッド プールがあります。ただし、スレッドが必要であることが確実な場合は、スレッドを停止する方法を知っておく必要があります。
私がスレッドに使用するパターンは次のとおりです。
public class Foo extends Thread {
private volatile boolean close = false;
public void run() {
while(!close) {
// do work
}
}
public void close() {
close = true;
// interrupt here if needed
}
}
上記のコード セグメントではclose
、while ループで読み取るスレッドは、 を呼び出すスレッドとは異なりますclose()
。volatile がないと、ループを実行しているスレッドは、閉じる変更を認識しない可能性があります。
同期の必要がないことに注意してください
使用の一般的な例の 1 つは、スレッドを終了するフラグとして変数volatile
を使用することです。volatile boolean
スレッドを開始し、別のスレッドから安全に中断できるようにしたい場合は、スレッドに定期的にフラグをチェックさせることができます。停止するには、フラグを true に設定します。flag を作成することで、ブロックvolatile
を使用しなくても、チェックしているスレッドが次にチェックするときにフラグが設定されていることを確認できます。synchronized
はい、変更可能な変数に複数のスレッドからアクセスする場合は常に、volatile を使用する必要があります。通常、複数のアトミック操作 (変数を変更する前に変数の状態を確認するなど) を実行する必要があるため、あまり一般的なユースケースではありません。その場合は、代わりに同期ブロックを使用します。
long および double 変数型の読み取りおよび書き込み操作の処理については誰も言及していません。読み取りと書き込みは、アトミック操作であるために volatile キーワードを使用する必要がある long および double 変数型を除いて、参照変数とほとんどのプリミティブ変数のアトミック操作です。@リンク
揮発性
volatile
⇒ synchronized
【概要】
volatile
プログラマーに対して、値は常に最新であると言います。問題は、値がさまざまな種類のハードウェア メモリに保存される可能性があることです。たとえば、CPU レジスタ、CPU キャッシュ、RAM などがあります。CPU レジスタと CPU キャッシュは CPU に属し、マルチスレッド環境でレスキューされている RAM とは異なり、データを共有できません。
volatile
キーワードは、変数がRAM メモリから直接読み書きされることを示します。ある程度の計算フットプリントがあります
Java 5
[概要]volatile
をサポートすることで拡張happens-before
揮発性フィールドへの書き込みは、そのフィールドの後続のすべての読み取りの前に発生します。
Read is after write
volatile
キーワードは、複数のスレッドがいくつかの値を同時に書き込むことができる状況を解決しません。答えはキーワード【About】race condition
synchronized
その結果、1 つのスレッドが値を書き込みvolatile
、他のスレッドが値を読み取るだけの場合にのみ安全です。
私の意見では、volatile キーワードが使用されているスレッドの停止以外の 2 つの重要なシナリオは次のとおりです。
volatile
すべてのスレッド (それ自体を含む) がインクリメントしていることのみを保証します。例: カウンターは変数の同じ面を同時に見ます。同期またはアトミックまたはその他のものの代わりに使用されるのではなく、読み取りを完全に同期させます。他の Java キーワードと比較しないでください。以下の例が示すように、volatile 変数操作もアトミックであり、一度に失敗または成功します。
package io.netty.example.telnet;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static volatile int a = 0;
public static void main(String args[]) throws InterruptedException{
List<Thread> list = new ArrayList<Thread>();
for(int i = 0 ; i<11 ;i++){
list.add(new Pojo());
}
for (Thread thread : list) {
thread.start();
}
Thread.sleep(20000);
System.out.println(a);
}
}
class Pojo extends Thread{
int a = 10001;
public void run() {
while(a-->0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Main.a++;
System.out.println("a = "+Main.a);
}
}
}
volatile を入れても入れなくても、結果は常に異なります。ただし、以下のように AtomicInteger を使用すると、結果は常に同じになります。これは同期も同様です。
package io.netty.example.telnet;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static volatile AtomicInteger a = new AtomicInteger(0);
public static void main(String args[]) throws InterruptedException{
List<Thread> list = new ArrayList<Thread>();
for(int i = 0 ; i<11 ;i++){
list.add(new Pojo());
}
for (Thread thread : list) {
thread.start();
}
Thread.sleep(20000);
System.out.println(a.get());
}
}
class Pojo extends Thread{
int a = 10001;
public void run() {
while(a-->0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Main.a.incrementAndGet();
System.out.println("a = "+Main.a);
}
}
}
マルチスレッド アプリケーションを開発している場合は、'volatile' キーワード、または 'synchronized' と、自由に使用できるその他の同時実行制御ツールと手法を使用する必要があります。このようなアプリケーションの例は、デスクトップ アプリです。
アプリケーション サーバー (Tomcat、JBoss AS、Glassfish など) にデプロイされるアプリケーションを開発している場合、同時実行制御はアプリケーション サーバーによって既に対処されているため、自分で処理する必要はありません。実際、私の記憶が正しければ、Java EE 標準では、サーブレットと EJB での同時実行制御が禁止されています。これは、処理から解放されるはずの「インフラストラクチャ」レイヤーの一部であるためです。シングルトン オブジェクトを実装している場合にのみ、そのようなアプリで同時実行制御を行います。これは、Spring のようなフレームワークを使用してコンポーネントを編成する場合でも、すでに対処されています。
そのため、アプリケーションが Web アプリケーションであり、Spring や EJB などの IoC フレームワークを使用する Java 開発のほとんどの場合、「volatile」を使用する必要はありません。
はい、かなり頻繁に使用しています。マルチスレッド コードには非常に便利です。あなたが指摘した記事は良いものです。ただし、次の 2 つの重要な点に注意してください。
揮発性フィールドにアクセスするすべてのスレッドは、(潜在的に) キャッシュされた値を使用する代わりに、続行する前に現在の値を読み取ります。
メンバー変数のみが揮発性または一時的になることができます。
はいぜったいに。(Java だけでなく、C# でも同様です。) 特定のプラットフォームでアトミック操作であることが保証されている値 (int や boolean など) を取得または設定する必要がある場合がありますが、必須ではありません。スレッドロックのオーバーヘッド。volatile キーワードを使用すると、値を読み取ったときに、別のスレッドでの書き込みによって古くなったばかりのキャッシュされた値ではなく、現在の値を取得できるようになります。
揮発性変数は軽量の同期です。すべてのスレッド間で最新のデータの可視性が必要であり、原子性が損なわれる可能性がある場合、そのような状況では揮発性変数を優先する必要があります。揮発性変数の読み取りは、スレッドによって行われた最新の書き込みを常に返します。これは、それらがレジスターにも、他のプロセッサーが参照できないキャッシュにもキャッシュされていないためです。揮発性はロックフリーです。シナリオが上記の基準を満たす場合、揮発性を使用します。
volatile
以下は、他のスレッドからスレッドの実行を制御するために使用される for 変数の要件を示す非常に単純なコードです(これは、必要とされる 1 つのシナリオvolatile
です)。
// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
public static void main(String[] a) throws Exception {
Task task = new Task();
new Thread(task).start();
Thread.sleep(500);
long stoppedOn = System.nanoTime();
task.stop(); // -----> do this to stop the thread
System.out.println("Stopping on: " + stoppedOn);
}
}
class Task implements Runnable {
// Try running with and without 'volatile' here
private volatile boolean state = true;
private int i = 0;
public void stop() {
state = false;
}
@Override
public void run() {
while(state) {
i++;
}
System.out.println(i + "> Stopped on: " + System.nanoTime());
}
}
volatile
を使用しない場合: ' Stopping on: xxx ' の後でも ' Stopped on: xxx ' メッセージが表示されることはなく、プログラムは引き続き実行されます。
Stopping on: 1895303906650500
volatile
使用すると、' Stopped on: xxx ' がすぐに表示されます。
Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300