今日の仕事で、私volatile
はJavaでキーワードに出くわしました。あまり詳しくないので、次の説明を見つけました。
その記事で問題のキーワードを説明している詳細を考えて、それを使用したり、このキーワードを正しい方法で使用できるケースを見たりしたことがありますか?
今日の仕事で、私volatile
はJavaでキーワードに出くわしました。あまり詳しくないので、次の説明を見つけました。
その記事で問題のキーワードを説明している詳細を考えて、それを使用したり、このキーワードを正しい方法で使用できるケースを見たりしたことがありますか?
回答:
volatile
メモリの可視性に関するセマンティクスがあります。基本的に、volatile
書き込み操作が完了した後、フィールドの値はすべてのリーダー(特に他のスレッド)から見えるようになります。がなければvolatile
、読者は更新されていない値を確認できます。
あなたの質問に答えるには:はい、volatile
変数を使用して、一部のコードがループを継続するかどうかを制御します。ループはvolatile
値をテストし、そうであれば続行しますtrue
。条件はfalse
、 "stop"メソッドを呼び出すことで設定できます。ループはfalse
、stopメソッドが実行を完了した後で値をテストするときに確認して終了します。
私が強くお勧めする「Java Concurrency in Practice」という本は、の良い説明ですvolatile
。この本は、質問で参照されているIBMの記事を書いたのと同じ人物が書いています(実際、彼はその記事の最後で彼の本を引用しています)。私の使用volatile
は、彼の記事が「パターン1ステータスフラグ」と呼んでいるものです。
volatile
内部でどのように機能するかについて詳しく知りたい場合は、Javaメモリモデルを参照してください。そのレベルを超えたい場合は、Hennessy&Pattersonなどの優れたコンピューターアーキテクチャブックをチェックして、キャッシュの一貫性と一貫性について読んでください。
「…volatile修飾子は、フィールドを読み取るすべてのスレッドが最後に書き込まれた値を参照することを保証します。」 -Josh Bloch
を使用することを考えている場合は、アトミック動作を扱うvolatile
パッケージjava.util.concurrent
を読んでください。シングルトンパターン
に関するウィキペディアの投稿では、揮発性の使用が示されています。
volatile
とsynchronized
キーワードの両方があるのはなぜですか?
volatile
例をもう取り上げていません。それは見つけることができますアーカイブされたバージョンで。
void
とpublic
キーワードの両方があるのですか」と言っているようなものです。
についての重要なポイントvolatile
:
synchronized
およびvolatile
andロックを使用することにより、Javaでの同期が可能です。synchronized
変数を使用できません。synchronized
変数でキーワードを使用することは違法であり、コンパイルエラーになります。synchronized
Javaで変数を使用する代わりに、Java volatile
変数を使用できます。これは、JVMスレッドにvolatile
メインメモリから変数の値を読み取り、ローカルにキャッシュしないように指示します。volatile
キーワードを使用する必要はありません。の使用例volatile
:
public class Singleton {
private static volatile Singleton _instance; // volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
最初のリクエストが来たときにインスタンスを遅延して作成しています。
我々は作成しない場合は_instance
、変数volatile
のインスタンスを作成されたスレッド、その後にSingleton
他のスレッドに通信することができません。したがって、スレッドAがシングルトンインスタンスを作成していて、作成直後にCPUが破損するなどの場合、他のすべてのスレッドは_instance
nullでないの値を確認できず、nullが割り当てられていると信じます。
なぜこれが起こるのですか?リーダースレッドはロックを行わず、ライタースレッドが同期されたブロックから出るまで、メモリは同期_instance
されず、メインメモリのの値は更新されません。JavaのVolatileキーワードを使用すると、これはJava自体によって処理され、そのような更新はすべてのリーダースレッドで表示されます。
結論:
volatile
キーワードは、スレッド間でメモリの内容を通信するためにも使用されます。
揮発性なしの使用例:
public class Singleton{
private static Singleton _instance; //without volatile variable
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null) _instance = new Singleton();
}
}
return _instance;
}
上記のコードはスレッドセーフではありません。JITコンパイラは、同期されたブロック内でインスタンスの値をもう一度チェックしますが(パフォーマンス上の理由から)、コンストラクタが実行を完了する前にインスタンスへの参照が設定されるようにバイトコードを再配置できます。つまり、getInstance()メソッドは、完全に初期化されていない可能性があるオブジェクトを返します。コードをスレッドセーフにするために、インスタンス変数にJava 5以降のキーワードvolatileを使用できます。volatileとマークされた変数は、オブジェクトのコンストラクターがその実行を完全に終了した後でのみ、他のスレッドに表示されます。
ソース
volatile
Javaでの使用:
フェイルファストイテレータは通常volatile
、リストオブジェクトのカウンタを使用して実装されます。
Iterator
作成され、カウンタの現在の値が中に埋め込まれているIterator
オブジェクト。Iterator
操作が行われ、方法は、2つのカウンタ値を比較し、スローConcurrentModificationException
それらが異なる場合。フェイルセーフイテレータの実装は通常、軽量です。それらは通常、特定のリスト実装のデータ構造のプロパティに依存しています。一般的なパターンはありません。
private static final Singleton _instance;
も同様です。
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がないと、ループを実行しているスレッドは、変更が終了するのを見ることはありません。
同期の必要がないことに注意してください
volatile
キーワードがないコードを使用した(そして他のユーザーが使用したのを見た)ことを知っているので、私は興味があります。
volatile
キーワードで宣言された変数は、それを特別にする2つの主要な性質を持っています。
揮発性変数がある場合、どのスレッドからもコンピュータ(マイクロプロセッサ)のキャッシュメモリにキャッシュすることはできません。アクセスは常にメインメモリから行われました。
ある場合は、書き込み動作 volatile変数に行くと、突然の読み取り操作が要求され、あることが保証され、書き込み動作は、前の読み取り操作に完成となります。
上記の2つの特性は、
一方で、
volatile
と、キーワードは、n個のリーダースレッドとそれにアクセスする1つのライタースレッドのみを持つ共有変数を維持する理想的な方法であることがわかります。volatile
キーワードを追加したら完了です。スレッドセーフティに関するその他のオーバーヘッドはありません。逆に、
私たちはできませんを使用しますvolatile
持っている共有変数を満たすために、単にキーワードをそれにアクセスする複数のライター・スレッドを。
longおよびdouble変数型の読み取りおよび書き込み操作の扱いについては誰も言及していません。読み取りと書き込みは、参照変数とほとんどのプリミティブ変数に対するアトミック操作です。ただし、long変数型とdouble変数型は除きます。これらは、volatileキーワードを使用してアトミック操作にする必要があります。@リンク
私の意見では、volatileキーワードが使用されているスレッドの停止以外の2つの重要なシナリオは次のとおりです。
マルチスレッドアプリケーションを開発している場合は、「volatile」キーワード、または「synchronized」などの同時実行制御ツールと、自由に使用できるテクニックを使用する必要があります。このようなアプリケーションの例はデスクトップアプリです。
アプリケーションサーバー(Tomcat、JBoss AS、Glassfishなど)にデプロイされるアプリケーションを開発している場合は、アプリケーションサーバーによって既に処理されているため、同時実行制御を自分で処理する必要はありません。実際、私がJava EE標準を正しく覚えていれば、サーブレットとEJBでの同時実行制御は禁止されています。これは、それが処理する必要のない「インフラストラクチャ」層の一部だからです。シングルトンオブジェクトを実装する場合にのみ、このようなアプリで同時実行制御を行います。これは、Springのようなフレームワークを使用してコンポーネントを編成した場合でも、すでに対処されています。
したがって、アプリケーションがWebアプリケーションであり、SpringやEJBのようなIoCフレームワークを使用するJava開発のほとんどの場合、「volatile」を使用する必要はありません。
volatile
すべてのスレッドが、それ自体であっても、増加することを保証するだけです。たとえば、カウンターは変数の同じ面を同時に見ます。同期またはアトミックなどの代わりに使用されるのではなく、読み取りを完全に同期させます。他のJavaキーワードと比較しないでください。以下の例に示すように、揮発性変数の操作もアトミックであり、一度に失敗または成功します。
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);
}
}
}
揮発性を入れても入れなくても、結果は常に異なります。ただし、以下のように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);
}
}
}
はい、私はそれをかなり頻繁に使用します-マルチスレッドのコードに非常に役立ちます。あなたが指摘した記事は良いものです。ただし、2つの重要な点に注意してください。
volatileキーワードの使用法は2つあります。
JVMがレジスターの値を読み取らないようにし、その値を強制的にメモリーから読み取ります。
ビジーフラグは、デバイスがビジー状態であるとフラグをロックによって保護されていない状態の継続からスレッドを防止するために使用されます。
while (busy) {
/* do something else */
}
別のスレッドがオフになると、テストスレッドが継続されます忙しいフラグ:
busy = 0;
ただし、ビジーはテストスレッドで頻繁にアクセスされるため、JVMはビジーの値をレジスタに配置してテストを最適化し、テストの前にメモリのビジーの値を読み取らずにレジスタの内容をテストします。テストスレッドはビジー状態の変更を認識せず、他のスレッドはメモリ内のビジー状態の値のみを変更するため、デッドロックが発生します。busyフラグをvolatileとして宣言すると、各テストの前にその値が読み取られます。
メモリの一貫性エラーのリスクを軽減します。
揮発性変数への書き込みは、同じ変数の後続の読み取りと「前に発生」の関係を確立するため 、揮発性変数を使用すると、メモリ整合性エラーのリスクが軽減されます。これは、揮発性変数への変更は常に他のスレッドから見えることを意味します。
メモリの一貫性エラーなしで読み取り、書き込みを行う手法は、アトミックアクションと呼ばれます。ます。
アトミックアクションは、一度に効果的に発生するアクションです。アトミックアクションは途中で停止することはできません。完全に発生するか、まったく発生しません。アトミックアクションの副作用は、アクションが完了するまで表示されません。
以下は、アトミックであると指定できるアクションです。
乾杯!
揮発性は以下を行います。
1>異なるスレッドによる揮発性変数の読み取りおよび書き込みは、スレッド自体のキャッシュまたはCPUレジスタからではなく、常にメモリから行われます。したがって、各スレッドは常に最新の値を扱います。2> 2つの異なるスレッドがヒープ内の同じインスタンスまたは静的変数を処理する場合、他のアクションが順不同に見える場合があります。これに関するジェレミー・マンソンのブログを見てください。しかし、揮発性はここで役立ちます。
以下の完全に実行中のコードは、同期キーワードを使用せずに、いくつかのスレッドが事前定義された順序で実行し、出力を印刷する方法を示しています。
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
これを達成するために、以下の本格的な実行コードを使用できます。
public class Solution {
static volatile int counter = 0;
static int print = 0;
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread[] ths = new Thread[4];
for (int i = 0; i < ths.length; i++) {
ths[i] = new Thread(new MyRunnable(i, ths.length));
ths[i].start();
}
}
static class MyRunnable implements Runnable {
final int thID;
final int total;
public MyRunnable(int id, int total) {
thID = id;
this.total = total;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
if (thID == counter) {
System.out.println("thread " + thID + " prints " + print);
print++;
if (print == total)
print = 0;
counter++;
if (counter == total)
counter = 0;
} else {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// log it
}
}
}
}
}
}
次のgithubリンクには、適切な説明が記載されているreadmeがあります。 https://github.com/sankar4git/volatile_thread_ordering
volatile
プログラマーにとって、値は常に最新であると言います。問題は、値がさまざまな種類のハードウェアメモリに保存できることです。たとえば、CPUレジスタ、CPUキャッシュ、RAMなどです。СPUレジスタとCPUキャッシュはCPUに属し、マルチスレッド環境で救済されているRAMとは異なり、データを共有できません。
volatile
キーワードは、変数がRAMメモリに直接読み書きされることを示します。いくつかの計算フットプリントがあります
Java 5
volatile
サポートについて拡張happens-before
[概要]
揮発性フィールドへの書き込みは、そのフィールドの後続のすべての読み取りの前に行われます。
volatile
キーワードは硬化しないrace condition
いくつかのスレッドができる状況書き同時にいくつかの値を。答えはsynchronized
キーワードです
その結果、1つのスレッドが書き込みを行い、他のスレッドがvolatile
値を読み取る場合にのみ安全です。
Oracleのドキュメントページから、メモリの一貫性の問題を修正するために、揮発性変数の必要性が生じています。
揮発性変数を使用すると、揮発性変数への書き込みにより、同じ変数の後続の読み取りとの発生前の関係が確立されるため、メモリ整合性エラーのリスクが軽減されます。
これは、volatile
変数への変更が他のスレッドから常に見えることを意味します。また、スレッドが揮発性変数を読み取るときに、最新の変更だけではなく、volatile
もたらしたコードの副作用ます。
で説明されているように Peter Parker
回答で、volatile
修飾子がない場合、各スレッドのスタックは独自の変数のコピーを持つことができます。変数をvolatile
、メモリの一貫性の問題が修正されました。
ジェンコフを見てくださいよりよく理解するためにチュートリアルページをください。
揮発性と揮発性を使用する使用例の詳細については、関連するSEの質問を参照してください。
1つの実用的な使用例:
たとえば、現在の時刻を特定の形式で出力する必要があるスレッドが多数ありますjava.text.SimpleDateFormat("HH-mm-ss")
。Yonは1つのクラスを持つことができ、現在の時刻をSimpleDateFormat
1秒ごとに変数に変換して更新します。他のすべてのスレッドは、この揮発性変数を使用して、現在の時刻をログファイルに出力できます。
揮発性キーを変数と共に使用すると、この変数を読み取るスレッドが同じ値を確実に参照できるようになります。変数の読み取りと書き込みを行う複数のスレッドがある場合、変数を揮発性にするだけでは不十分で、データが破損します。イメージスレッドは同じ値を読み取りましたが、それぞれがいくつかの変更を加えました(たとえば、カウンターをインクリメントしました)。メモリに書き戻すと、データの整合性が損なわれます。そのため、変数を同期させる必要があります(異なる方法が可能です)。
変更が1つのスレッドによって行われ、他のスレッドがこの値を読み取るだけでよい場合は、volatileが適しています。
以下は、volatile
他のスレッドからのスレッド実行を制御するために使用されるfor変数の要件を示す非常に単純なコードです(これは、volatile
必要なシナリオの1つです)。
// 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
使用されていない:あなたは「見ることは決してないだろう:XXXで停止しても」の後のメッセージ「に停止:XXX」、およびプログラムが実行を継続します。
Stopping on: 1895303906650500
volatile
使用時:「Stopped on:xxx」がすぐに表示されます。
Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300
volatile
JSR 133で定義された新しいJavaメモリモデルに付属している重要なプロパティを省略しています。スレッドがvolatile
変数を読み取るとき、他のスレッドによって最後に書き込まれた値だけでなく、他の変数への他のすべての書き込みも確認します。volatile
書き込み時にその別のスレッドに表示されていました。参照してくださいこの答えと、この参照を。