java執行緒安全問題之靜態變數、例項變數、區域性變數

王小胖醬發表於2019-01-23

靜態變數:執行緒非安全。

靜態變數即類變數,位於方法區,為所有物件共享,共享一份記憶體,一旦靜態變數被修改,其他物件均對修改可見,故執行緒非安全。

例項變數:單例模式(只有一個物件例項存在)執行緒非安全,非單例執行緒安全。

例項變數為物件例項私有,在虛擬機器的堆中分配,若在系統中只存在一個此物件的例項,在多執行緒環境下,“猶如”靜態變數那樣,被某個執行緒修改後,其他執行緒對修改均可見,故執行緒非安全;如果每個執行緒執行都是在不同的物件中,那物件與物件之間的例項變數的修改將互不影響,故執行緒安全。

區域性變數:執行緒安全。

每個執行緒執行時將會把區域性變數放在各自棧幀的工作記憶體中,執行緒間不共享,故不存線上程安全問題。

靜態變數執行緒安全問題模擬:

package com.wxx.demo;


/**
 * @Author : leisure
 * @Date : 2019/1/23
 */
public class VariableTest implements Runnable{

    private static int static_i ;


    @Override
    public void run() {
        static_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i 的值:" + static_i);
        static_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i*2的值:" + static_i * 2);
    }

    public static void main(String[] args) {
        VariableTest t = new VariableTest();
        //啟動儘量多的執行緒才能很容易的模擬問題
        for (int i = 0; i < 3000; i++)
        {
            //t可以換成new Test(),保證每個執行緒都在不同的物件中執行,結果一樣
            new Thread(t, "執行緒" + i).start();
        }
    }
}

複製程式碼

執行結果:

[執行緒1479]獲取static_i*2的值:20
[執行緒1483]獲取static_i 的值:4
[執行緒1483]獲取static_i*2的值:8
[執行緒2891]獲取static_i 的值:4
[執行緒2891]獲取static_i*2的值:20
[執行緒1689]獲取static_i 的值:10
[執行緒1689]獲取static_i*2的值:20
複製程式碼

根據程式碼註釋中模擬的情況,當執行緒1執行了static_i = 4; static_i = 10; 後,執行緒2獲得執行權,static_i = 4; 然後當執行緒1獲得執行權執行static_i * 2; 必然輸出結果4*2=8,按照這個模擬,我們可能會在控制檯看到輸出為8的結果。或者 當執行緒1執行了static_i = 4; static_i = 10; 後,執行緒2獲得執行權,我們可能會在控制檯看到輸出為獲取static_i 的值:10的結果

 例項變數執行緒安全問題模擬:

package com.wxx.demo;


/**
 * @Author : leisure
 * @Date : 2019/1/23
 */
public class VariableTest implements Runnable{

    private  int static_i ;


    @Override
    public void run() {
        static_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i 的值:" + static_i);
        static_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i*2的值:" + static_i * 2);
    }

    public static void main(String[] args) {
        VariableTest t = new VariableTest();
        //啟動儘量多的執行緒才能很容易的模擬問題
        for (int i = 0; i < 3000; i++)
        {
            //t可以換成new Test(),保證每個執行緒都在不同的物件中執行,結果一樣
            new Thread(t, "執行緒" + i).start();
        }
    }
}


複製程式碼

理由同上

區域性變數執行緒安全問題模擬:

package com.wxx.demo;


/**
 * @Author : leisure
 * @Date : 2019/1/23
 */
public class VariableTest implements Runnable{

    private  int static_i ;


    @Override
    public void run() {
        int static_i ;
        static_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i 的值:" + static_i);
        static_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取static_i*2的值:" + static_i * 2);
    }

    public static void main(String[] args) {
        VariableTest t = new VariableTest();
        //啟動儘量多的執行緒才能很容易的模擬問題
        for (int i = 0; i < 3000; i++)
        {
            //t可以換成new Test(),保證每個執行緒都在不同的物件中執行,結果一樣
            new Thread(t, "執行緒" + i).start();
        }
    }
}



複製程式碼

控制檯沒有出現異常資料。


靜態方法是執行緒安全的

先看一個類

public class Test{

public static String hello(String str){

String tmp="";

tmp = tmp+str;

return tmp;

}

}

hello方法會不會有多執行緒安全問題呢?沒有!!

靜態方法如果沒有使用靜態變數,則沒有執行緒安全問題。

為什麼呢?因為靜態方法內宣告的變數,每個執行緒呼叫時,都會新建立一份,而不會共用一個儲存單元。比如這裡的tmp,每個執行緒都會建立自己的一份,因此不會有執行緒安全問題

注意,靜態變數,由於是在類載入時佔用一個儲存區,每個執行緒都是共用這個儲存區的,所以如果在靜態方法裡使用了靜態變數,這就會有執行緒安全問題!

相關文章