java多線程總結-同步之volatile關鍵字

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;,就會達到預想中的效果安全

思考:爲何加上這個關鍵字,其餘線程就會讀取到已經改變的變量的值了?ide

是由於在CPU計算過程當中,會將計算過程須要的數據加載到CPU計算緩存中,當CPU計算中斷時,有可能刷新緩存,從新讀取內存中的數據。在線程運行的過程當中,若是某變量被其餘線程修改,可能形成數據不一致的狀況,從而致使結果錯誤。
而volatile修飾的變量是線程可見的,當JVM解釋volatile修飾的變量時,會通知CPU,在計算過程當中,每次使用變量參與計算時,都會檢查內存中的數據是否發生變化,而不是一直使用CPU緩存中的數據,能夠保證計算結果的正確。atom

可是這樣還有一個問題,volatile只能保證可見性,不能保證原子性線程

2 案例之變量的原子性

下面再看一個示例:
預期結果:起10個線程,每一個線程都對count增長10000,預期結果爲count=100000code

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);
    }
}

但結果並非blog

緣由是volatile只是通知底層計算時,CPU檢查內存數據,而不是讓一個變量在多個線程中同步。內存

這時候能夠給m()方法增長一個synchronized關鍵字,能夠達到預期的效果,即synchronized void m()rem

還有另外一種方法能夠保證原子性,在上面代碼將count聲明爲AtomicInteger原子操做,結果仍然是100000

// 其中的每一個方法都是原子操做。能夠保證線程安全。
    AtomicInteger count = new AtomicInteger(0);
    void m(){
        for(int i = 0; i < 10000; i++){
            count.incrementAndGet();
        }
    }

這裏不單單可聲明Integer類型,java.util.concurrent.atomic包裏面還有其餘類型的

相關文章
相關標籤/搜索