java多線程編程中,存在不少線程安全問題,至於什麼是線程安全呢,給出一個通俗易懂的概念仍是蠻難的,如同《java併發編程實踐》中所說:java
寫道編程
給線程安全下定義比較困難。存在不少種定義,如:「一個類在能夠被多個線程安全調用時就是線程安全的」。
安全
此處不贅述了,首先給出靜態變量、實例變量、局部變量在多線程環境下的線程安全問題結論,而後用示例驗證,請你們擦亮眼睛,有錯必究,不然誤人子弟!多線程
靜態變量:線程非安全。併發
靜態變量即類變量,位於方法區,爲全部對象共享,共享一分內存,一旦靜態變量被修改,其餘對象均對修改可見,故線程非安全。字體
實例變量:單例模式(只有一個對象實例存在)線程非安全,非單例線程安全。spa
實例變量爲對象實例私有,在虛擬機的堆中分配,若在系統中只存在一個此對象的實例,在多線程環境下,「猶如」靜態變量那樣,被某個線程修改後,其餘線程對修改都可見,故線程非安全;若是每一個線程執行都是在不一樣的對象中,那對象與對象之間的實例變量的修改將互不影響,故線程安全。線程
局部變量:線程安全。code
每一個線程執行時將會把局部變量放在各自棧幀的工做內存中,線程間不共享,故不存在線程安全問題。orm
靜態變量線程安全問題模擬:
----------------------------------------------------------------------------------
/** * 線程安全問題模擬執行 * ------------------------------ * 線程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
看紅色標註的部分,確實出現了咱們的預想,一樣也證實了咱們的結論。
實例變量線程安全問題模擬:
----------------------------------------------------------------------------------
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();模擬非單例狀況,會發現不存在線程安全問題。
局部變量線程安全問題模擬:
----------------------------------------------------------------------------------
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(); } } }
控制檯沒有出現異常數據。
---------------------------------------------------------------
以上只是經過簡單的實例來展現靜態變量、實例變量、局部變量等的線程安全問題,
並未進行底層的分析,下一篇將對線程問題的底層進行剖析。