1 案例之變量內存可見性
代碼解析:新起一個子線程執行m()方法,1秒后主線程將b置為false,子線程是否會停止執行死循環while(b){},打印“end”
package com.bernardlowe.concurrent.t01;
import java.util.concurrent.TimeUnit;
public class Test_09 {
boolean b = true;
void m(){
System.out.println("start");
while(b){}
System.out.println("end");
}
public static void main(String[] args) {
final Test_09 t = new Test_09();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.b = false;
}
}
**結果:1秒鍾過后並不會停止執行死循環while(b){},打印“end” **
這時候,如果將boolean b = true;這段代碼前加一個volatile關鍵字
即volatile boolean b = true;
,就會達到預想中的效果
思考:為什么加上這個關鍵字,其他線程就會讀取到已經改變的變量的值了?
是因為在CPU計算過程中,會將計算過程需要的數據加載到CPU計算緩存中,當CPU計算中斷時,有可能刷新緩存,重新讀取內存中的數據。在線程運行的過程中,如果某變量被其他線程修改,可能造成數據不一致的情況,從而導致結果錯誤。
而volatile修飾的變量是線程可見的,當JVM解釋volatile修飾的變量時,會通知CPU,在計算過程中,每次使用變量參與計算時,都會檢查內存中的數據是否發生變化,而不是一直使用CPU緩存中的數據,可以保證計算結果的正確。
但是這樣還有一個問題,volatile只能保證可見性,不能保證原子性
2 案例之變量的原子性
下面再看一個示例:
預期結果:起10個線程,每個線程都對count增加10000,預期結果為count=100000
package com.bernardlowe.concurrent.t01;
import java.util.ArrayList;
import java.util.List;
public class Test_10 {
volatile int count = 0;
/*synchronized*/ void m(){
for(int i = 0; i < 10000; i++){
count++;
}
}
public static void main(String[] args) {
final Test_10 t = new Test_10();
List<Thread> threads = new ArrayList<>();
for(int i = 0; i < 10; i++){
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}));
}
for(Thread thread : threads){
thread.start();
}
for(Thread thread : threads){
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(t.count);
}
}
但結果並不是
原因是volatile只是通知底層計算時,CPU檢查內存數據,而不是讓一個變量在多個線程中同步。
這時候可以給m()方法增加一個synchronized關鍵字,可以達到預期的效果,即synchronized void m()
還有另一種方法可以保證原子性,在上面代碼將count聲明為AtomicInteger原子操作,結果仍然是100000
// 其中的每個方法都是原子操作。可以保證線程安全。
AtomicInteger count = new AtomicInteger(0);
void m(){
for(int i = 0; i < 10000; i++){
count.incrementAndGet();
}
}
這里不僅僅可聲明Integer類型,java.util.concurrent.atomic包里面還有其他類型的