Java Volatile 關鍵字

volatile

我們在代碼中開啓一個線程之後,想要依據某些條件去控制線程是否結束的時候,可以通過定義一個 volatile 關鍵字修飾的變量去進行操作。示例代碼如下:

public class RunThread extends Thread {

    private volatile boolean isRunning;

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("進入線程。。。");
        while (isRunning) {
            // do something
        }
        System.out.println("線程結束");
    }

    public static void main(String[] arg0) throws InterruptedException {
        RunThread t1 = new RunThread();
        t1.start();
        Thread.sleep(3000);
        t1.setRunning(false);
        Thread.sleep(1000);
        System.out.println("t1 isRunnung : " + t1.isRunning);
    }
}

打印結果:
這裏寫圖片描述

結果正常,沒毛病。
那麼我們多個線程調用呢?是否還能達到預期效果?我們來試一下:

public class RunThread1 extends Thread {

    private static volatile int count;

    private static void addCount() {
        for (int i = 0; i < 1000; i++) {
            count++;
        }
        System.out.println(count);
    }

    @Override
    public void run() {
        addCount();
    }

    public static void main(String[] arg0) {
        RunThread1[] ts = new RunThread1[10];
        for (int i = 0; i < 10; i++) {
            ts[i] = new RunThread1();
        }

        for (int i = 0; i < 10; i++) {
            ts[i].start();
        }
    }
}

運行結果:
這裏寫圖片描述

正常來說,我們最終的結果肯定要是 10000 纔是正常的,但是這個輸出的結果,明顯與我們的預期不相符。

驗證結果:volatile 線程間可見性,非原子性,屬於線程不安全。

Atomic

那麼如果我們上面的多線程調用情況我們要怎麼辦呢? 這裏提供一種寫法:

public class RunThread2 extends Thread {

    private static AtomicInteger count = new AtomicInteger();

    private static void addCount() {
        for (int i = 0; i < 1000; i++) {
            count.getAndIncrement();
        }
        System.out.println(count);
    }

    @Override
    public void run() {
        addCount();
    }

    public static void main(String[] arg0) {
        RunThread2[] ts = new RunThread2[10];
        for (int i = 0; i < 10; i++) {
            ts[i] = new RunThread2();
        }

        for (int i = 0; i < 10; i++) {
            ts[i].start();
        }
    }
}

打印結果:
這裏寫圖片描述

最終輸出結果,是不是有 10000 了, 有時候編譯器有些延遲,但是不影響我們每次的打印結果都有 10000 。

這裏簡略的介紹一下 Atomic 包:Java從JDK1.5開始提供了java.util.concurrent.atomic包,方便程序員在多線程環境下,無鎖的進行原子操作。原子變量的底層使用了處理器提供的原子指令,但是不同的CPU架構可能提供的原子指令不一樣,也有可能需要某種形式的內部鎖,所以該方法不能絕對保證線程不被阻塞

當然 Atomic 包中還有其他原子更新方式,比如 AtomicBoolean , AtomicLong , AtomicIntegerArray , AtomicLongArray , AtomicReferenceArray.. 等等。有需要的還請各位自行 Google.

學習筆記整理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章