java線程安全問題之靜態變量、實例變量、局部變量

 java多線程編程中,存在不少線程安全問題,至於什麼是線程安全呢,給出一個通俗易懂的概念仍是蠻難的,如同《java併發編程實踐》中所說:java

寫道編程

給線程安全下定義比較困難。存在不少種定義,如:「一個類在能夠被多個線程安全調用時就是線程安全的」。 
安全

 此處不贅述了,首先給出靜態變量、實例變量、局部變量在多線程環境下的線程安全問題結論,而後用示例驗證,請你們擦亮眼睛,有錯必究,不然誤人子弟!多線程

 

靜態變量:線程非安全。併發

靜態變量即類變量,位於方法區,爲全部對象共享,共享一分內存,一旦靜態變量被修改,其餘對象均對修改可見,故線程非安全。字體

實例變量:單例模式(只有一個對象實例存在)線程非安全,非單例線程安全。spa

實例變量爲對象實例私有,在虛擬機的堆中分配,若在系統中只存在一個此對象的實例,在多線程環境下,「猶如」靜態變量那樣,被某個線程修改後,其餘線程對修改都可見,故線程非安全;若是每一個線程執行都是在不一樣的對象中,那對象與對象之間的實例變量的修改將互不影響,故線程安全。線程

局部變量:線程安全。code

每一個線程執行時將會把局部變量放在各自棧幀的工做內存中,線程間不共享,故不存在線程安全問題。orm

 

靜態變量線程安全問題模擬:

----------------------------------------------------------------------------------

Java代碼  

/** 
  * 線程安全問題模擬執行 
  *  ------------------------------ 
  *       線程1      |    線程2 
  *  ------------------------------ 
  *   static_i = 4;  | 等待 
  *   static_i = 10; | 等待 
  *    等待          | static_i = 4; 
  *   static_i * 2;  | 等待 
  *  -----------------------------
 * */
public class Test implements Runnable
{
    private static int static_i;//靜態變量 
    
    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*3的值:" + static_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //啓動儘可能多的線程才能很容易的模擬問題 
        for (int i = 0; i < 3000; i++)
        {
            //t能夠換成new Test(),保證每一個線程都在不一樣的對象中執行,結果同樣 
            new Thread(t, "線程" + i).start();
        }
    }
}


 

根據代碼註釋中模擬的狀況,當線程1執行了static_i = 4;  static_i = 10; 後,線程2得到執行權,static_i = 4; 而後當線程1得到執行權執行static_i * 2;  必然輸出結果4*2=8,按照這個模擬,咱們可能會在控制檯看到輸出爲8的結果。

寫道

[線程27]獲取static_i 的值:4 
[線程22]獲取static_i*2的值:20 
[線程28]獲取static_i 的值:4 
[線程23]獲取static_i*2的值:8 
[線程29]獲取static_i 的值:4 
[線程30]獲取static_i 的值:4 
[線程31]獲取static_i 的值:4 
[線程24]獲取static_i*2的值:20

 看紅色標註的部分,確實出現了咱們的預想,一樣也證實了咱們的結論。

 

實例變量線程安全問題模擬:

----------------------------------------------------------------------------------

Java代碼  

public class Test implements Runnable
{
    private int instance_i;//實例變量
    
    public void run()
    {
        instance_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取instance_i 的值:" + instance_i);
        instance_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取instance_i*3的值:" + instance_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //啓動儘可能多的線程才能很容易的模擬問題 
        for (int i = 0; i < 3000; i++)
        {
            //每一個線程對在對象t中運行,模擬單例狀況
            new Thread(t, "線程" + i).start();
        }
    }
}


 

按照本文開頭的分析,猶如靜態變量那樣,每一個線程都在修改同一個對象的實例變量,確定會出現線程安全問題。

寫道

[線程66]獲取instance_i 的值:10 
[線程33]獲取instance_i*2的值:20 
[線程67]獲取instance_i 的值:4 
[線程34]獲取instance_i*2的值:8 
[線程35]獲取instance_i*2的值:20 
[線程68]獲取instance_i 的值:4

 

看紅色字體,可知單例狀況下,實例變量線程非安全。

 

將new Thread(t, "線程" + i).start();改爲new Thread(new Test(), "線程" + i).start();模擬非單例狀況,會發現不存在線程安全問題。

 

 

局部變量線程安全問題模擬:

----------------------------------------------------------------------------------

 

Java代碼  

public class Test implements Runnable
{
    public void run()
    {
        int local_i = 4;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取local_i 的值:" + local_i);
        local_i = 10;
        System.out.println("[" + Thread.currentThread().getName()
                + "]獲取local_i*2的值:" + local_i * 2);
    }
    
    public static void main(String[] args)
    {
        Test t = new Test();
        //啓動儘可能多的線程才能很容易的模擬問題
        for (int i = 0; i < 3000; i++)
        {
            //每一個線程對在對象t中運行,模擬單例狀況 
            new Thread(t, "線程" + i).start();
        }
    }
}


控制檯沒有出現異常數據。

 

---------------------------------------------------------------

以上只是經過簡單的實例來展現靜態變量、實例變量、局部變量等的線程安全問題,

並未進行底層的分析,下一篇將對線程問題的底層進行剖析。

相關文章
相關標籤/搜索