java多線程-慎重使用volatile關鍵字

Java語言包含兩種內在的同步機制:同步塊(或方法)和 volatile 變量。這兩種機制的提出都是爲了實現代碼線程的安全性。其中 Volatile 變量的同步性較差(但有時它更簡單而且開銷更低),並且其使用也更容易出錯。html

public volatile boolean exit = false;
在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個線程來修改exit的值.安全

volatile關鍵字用於聲明簡單類型變量,如int、float、boolean等數據類型。若是這些簡單數據類型聲明爲volatile,對它們的操做就會變成原子級別的。但這有必定的限制。例如,下面的例子中的n就不是原子級別的:spa

複製代碼
package junit.test;

public class ThreadVolatile extends Thread {
    public static volatile int n = 0;

    public void run() {
        for (int i = 0; i < 10; i++)
            try {
                n = n + 1;
                sleep(3); // 爲了使運行結果更隨機,延遲3毫秒

            } catch (Exception e) {
            }
    }

    public static void main(String[] args) throws Exception {

        Thread threads[] = new Thread[100];
        for (int i = 0; i < threads.length; i++)
            // 創建100個線程
            threads[i] = new ThreadVolatile();
        for (int i = 0; i < threads.length; i++)
            // 運行剛纔創建的100個線程
            threads[i].start();
        for (int i = 0; i < threads.length; i++)
            // 100個線程都執行完後繼續
            threads[i].join();
        System.out.println("n=" + ThreadVolatile.n);
    }
}
複製代碼

若是對n的操做是原子級別的,最後輸出的結果應該爲n=1000,而在執行上面代碼時,不少時侯輸出的n都小於1000,這說明n=n+1不是原子級別的操做。緣由是聲明爲volatile的簡單變量若是當前值由該變量之前的值相關,那麼volatile關鍵字不起做用,也就是說以下的表達式都不是原子操做:
n = n + 1;
n++;
若是要想使這種狀況變成原子操做,須要使用synchronized關鍵字,如上的代碼能夠改爲以下的形式:線程

複製代碼
package junit.test;

public class ThreadVolatile2 extends Thread {
    public static volatile int n = 0;

    public static synchronized void inc() {
        n++;
    }

    public void run() {
        for (int i = 0; i < 10; i++)
            try {
                inc(); // n = n + 1 改爲了 inc();
                sleep(3); // 爲了使運行結果更隨機,延遲3毫秒

            } catch (Exception e) {
            }
    }

    public static void main(String[] args) throws Exception {

        Thread threads[] = new Thread[100];
        for (int i = 0; i < threads.length; i++)
            // 創建100個線程
            threads[i] = new ThreadVolatile2();
        for (int i = 0; i < threads.length; i++)
            // 運行剛纔創建的100個線程
            threads[i].start();
        for (int i = 0; i < threads.length; i++)
            // 100個線程都執行完後繼續
            threads[i].join();
        System.out.println("n=" + ThreadVolatile2.n);
    }
}
複製代碼

上面的代碼將n=n+1改爲了inc(),其中inc方法使用了synchronized關鍵字進行方法同步。所以,在使用volatile關鍵字時要慎重,並非只要簡單類型變量使用volatile修飾,對這個變量的全部操做都是原來操做,當變量的值由自身的上一個決定時,如n=n+一、n++等,volatile關鍵字將失效,只有當變量的值和自身上一個值無關時對該變量的操做纔是原子級別的,如n = m + 1,這個就是原級別的。因此在使用volatile關鍵時必定要謹慎,若是本身沒有把握,可使用synchronized來代替volatile。code

 

本文轉自http://www.cnblogs.com/linjiqin/p/3212737.html 感謝原做者htm

相關文章
相關標籤/搜索