javaにおけるvolatileキーワードの意味
5822 ワード
javaスレッドの同時処理には、現在大きな混淆が存在しているキーワードがあります。このキーワードを使って、マルチスレッドの同時処理を行う時に、万事がうまくいくと思います。
Java言語はマルチスレッドをサポートしており、スレッド合併の問題を解決するために、言語内部に同期ブロックとvolatileキーワード機構を導入している。
synchronized
同期ブロックはみんなよく知っています。synchronizedキーワードによって実現されます。すべてsynchronizedとブロックステートメントを加えて、マルチスレッドを訪問する時、同じ時間にスレッドが一つしか使えません。
synchronized修飾の方法またはコードブロック。
volatile
volatileで修飾された変数は、スレッドが変数を使うたびに、変数修正後の一番近い値を読みます。volatileは誤用されやすく、原子的操作に使われます。
次の例を見て、私達はカウンタを実現します。スレッドが起動するたびにカウンタinc方法を呼び出して、カウンタを加算します。
実行環境——jdkバージョン:jdk 1.6.0_31,メモリ:3 G cpu:x 86 2.4 G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
運行結果はまだ期待されていないです。原因を分析します。
javaゴミ回収整理の文では、jvmの運行時刻のメモリの割り当てが述べられています。メモリ領域の一つはjvm仮想マシンスタックであり、各スレッドが実行される時にはスレッドスタックがあります。
スレッドスタックは、スレッド実行時の変数値情報を保存しています。スレッドがあるオブジェクトにアクセスしたときの値は、まずオブジェクトの参照によって、ヒープメモリに対応する変数の値を見つけて、メモリを積みます。
変数の具体的な値はスレッドローカルメモリにロードされ、変数コピーを作成した後、スレッドはスタック内のオブジェクトのメモリ変数値とは関係なく、コピー変数の値を直接修正します。
修正後のある時刻(スレッドが終了する前)に、スレッド変数のコピーの値を自動的にオブジェクトに戻します。このように積み重ねられているオブジェクトの値が変わります。次の図
この書き込みのインタラクティブを説明します
read and loadは、マスタから現在の作業メモリuse and assignまでコピーします。 コードを実行し、共有変数の値を変更します。 store and writeはワークメモリデータで既存の内容を更新します。
その中にはuse and assignが何度も現れます。
しかし、これらの動作は原子性ではなく、つまりread loadの後、主メモリcount変数が修正された後、スレッド作業メモリの値は既にロードされているため、対応する変化が発生しないため、計算された結果は予想と違っています。
volatile修飾の変数については、jvm仮想マシンは、メインメモリからスレッドワークメモリへのロードを保証するだけの値が最新のものです。
例えばスレッド1、スレッド2がread、ロード操作を行っている場合、メインメモリのcountの値が5であることが分かります。この最新の値がロードされます。
スレッド1山のcountを修正すると、writeがメインメモリに入り、メインメモリのcount変数が6になります。
スレッド2はすでにreadされていますので、ロード操作は、演算後もメインメモリcountの変数値が6に更新されます。
二つのスレッドが直ちにvolatileキーワードで修正された後も、同時に発生する場合があります。
Java言語はマルチスレッドをサポートしており、スレッド合併の問題を解決するために、言語内部に同期ブロックとvolatileキーワード機構を導入している。
synchronized
同期ブロックはみんなよく知っています。synchronizedキーワードによって実現されます。すべてsynchronizedとブロックステートメントを加えて、マルチスレッドを訪問する時、同じ時間にスレッドが一つしか使えません。
synchronized修飾の方法またはコードブロック。
volatile
volatileで修飾された変数は、スレッドが変数を使うたびに、変数修正後の一番近い値を読みます。volatileは誤用されやすく、原子的操作に使われます。
次の例を見て、私達はカウンタを実現します。スレッドが起動するたびにカウンタinc方法を呼び出して、カウンタを加算します。
実行環境——jdkバージョン:jdk 1.6.0_31,メモリ:3 G cpu:x 86 2.4 G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public
class
Counter {
public
static
int
count =
0
;
public
static
void
inc() {
// 1 ,
try
{
Thread.sleep(
1
);
}
catch
(InterruptedException e) {
}
count++;
}
public
static
void
main(String[] args) {
// 1000 , i++ ,
for
(
int
i =
0
; i <
1000
; i++) {
new
Thread(
new
Runnable() {
@Override
public
void
run() {
Counter.inc();
}
}).start();
}
// , 1000
System.out.println(
" :Counter.count="
+ Counter.count);
}
}
11
:Counter.count=
995
1 , : :Counter.count=
995
, , ,Counter.count
1000
11
, , count
volatile
, ,
12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public
class
Counter {
public
volatile
static
int
count =
0
;
public
static
void
inc() {
// 1 ,
try
{
Thread.sleep(
1
);
}
catch
(InterruptedException e) {
}
count++;
}
public
static
void
main(String[] args) {
// 1000 , i++ ,
for
(
int
i =
0
; i <
1000
; i++) {
new
Thread(
new
Runnable() {
@Override
public
void
run() {
Counter.inc();
}
}).start();
}
// , 1000
System.out.println(
" :Counter.count="
+ Counter.count);
}
}
運転結果:Counter.com unt=992運行結果はまだ期待されていないです。原因を分析します。
javaゴミ回収整理の文では、jvmの運行時刻のメモリの割り当てが述べられています。メモリ領域の一つはjvm仮想マシンスタックであり、各スレッドが実行される時にはスレッドスタックがあります。
スレッドスタックは、スレッド実行時の変数値情報を保存しています。スレッドがあるオブジェクトにアクセスしたときの値は、まずオブジェクトの参照によって、ヒープメモリに対応する変数の値を見つけて、メモリを積みます。
変数の具体的な値はスレッドローカルメモリにロードされ、変数コピーを作成した後、スレッドはスタック内のオブジェクトのメモリ変数値とは関係なく、コピー変数の値を直接修正します。
修正後のある時刻(スレッドが終了する前)に、スレッド変数のコピーの値を自動的にオブジェクトに戻します。このように積み重ねられているオブジェクトの値が変わります。次の図
この書き込みのインタラクティブを説明します
read and loadは、マスタから現在の作業メモリuse and assignまでコピーします。 コードを実行し、共有変数の値を変更します。 store and writeはワークメモリデータで既存の内容を更新します。
その中にはuse and assignが何度も現れます。
しかし、これらの動作は原子性ではなく、つまりread loadの後、主メモリcount変数が修正された後、スレッド作業メモリの値は既にロードされているため、対応する変化が発生しないため、計算された結果は予想と違っています。
volatile修飾の変数については、jvm仮想マシンは、メインメモリからスレッドワークメモリへのロードを保証するだけの値が最新のものです。
例えばスレッド1、スレッド2がread、ロード操作を行っている場合、メインメモリのcountの値が5であることが分かります。この最新の値がロードされます。
スレッド1山のcountを修正すると、writeがメインメモリに入り、メインメモリのcount変数が6になります。
スレッド2はすでにreadされていますので、ロード操作は、演算後もメインメモリcountの変数値が6に更新されます。
二つのスレッドが直ちにvolatileキーワードで修正された後も、同時に発生する場合があります。