java併發程式設計(一)-從入門到吐血
阿新 • • 發佈:2019-01-11
一:初始原子類
在併發程式設計中,若多個執行緒同時操作同一變數就有問題了,比如:
public class Calculate {
private int num;
public void add() {
try {
for(int i=0;i<200;i++){
Thread.sleep(100);num++;
System.out.println(num);}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面寫了一個類,裡面有一個迴圈200次自增1的方法,這時我們啟動三個執行緒同時呼叫這個方法,我們都知道,會有執行緒安全問題,如下:
public class Test {
public static void main(String[] args){
Calculate calculate = new Calculate();
for(int i=0;i<3;i++){
new Thread(){
@Override
public void run() {
calculate .add();
}
}.start();
}}
}
正常的話最後num會變成600,但實際上卻不是,會比600小,因為會出現多個執行緒同時修改了這個數。
這時我們一般會在 產生競爭條件的地方加上鎖,也就是 num++這裡;
synchronized (this) {
num++;
System.out.println(num);
}
這樣可以解決問題,不過我們都知道加鎖效率會變低,其實java1.5後給我們提供了原子類,顧名思義,該操作是原子性的,其粒度更細,因為競爭範圍縮小到了單個變數上。
原子操作類都在下面這個包下:java.util.concurrent.atomic,每個類都有各自的方法。
所以上面Calculate類可以改成:其中
new AtomicInteger();//括號裡可以填初始值
public class Calculate {
private AtomicInteger num = new AtomicInteger(); public void add() {
try {
for(int i=0;i<200;i++){
Thread.sleep(100);
System.out.println(num.getAndIncrement());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
測試發現一切正常。
二:Volatile-保證多執行緒中變數的可見性
先看以下程式碼:
public class Test { private static boolean boo; public static void main(String[] args) throws InterruptedException { new Thread(){ @Override public void run() { for(;;){ if(boo){ System.out.println(boo+"==="); System.exit(0); } } } }.start(); Thread.sleep(50); new Thread(){ @Override public void run() { for(;;){ boo = true; } } }.start();}正常來說這段程式碼控制檯最終肯定會有輸出的,只是時間長短問題,但經過測試我們會發現,程式會卡死,這是為什麼呢?
由於CPU速度太快,而讀記憶體很慢,所有在CPU和主記憶體之間會有快取,上面的程式中第一個執行緒從快取中取到boo的值是false,它不會去讀記憶體,不知道boo的值已經改變了,所以會一直執行下去,但如果在變數前加上volatile關鍵字,則程式就可以結束並有結果輸出。
因為加了volatile後,就可以保證變數的修改讓所有執行緒看見!關於volatile關鍵字的詳細,可參看下面這篇文章:
http://www.cnblogs.com/xrq730/p/7048693.html