1. 程式人生 > >java併發程式設計(一)-從入門到吐血

java併發程式設計(一)-從入門到吐血

一:初始原子類

在併發程式設計中,若多個執行緒同時操作同一變數就有問題了,比如:

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