執行緒安全和執行緒不安全理解

weixin_34391854發表於2014-10-07
執行緒安全就是多執行緒訪問時,採用了加鎖機制,當一個執行緒訪問該類的某個資料時,進行保護,其它執行緒不能進行訪問直到該執行緒讀取完,其它執行緒才可使用。不會出現資料不一致或者資料汙染。
      執行緒不安全就是不提供資料訪問保護,有可能出現多個執行緒先後更改資料造成所得到的資料是髒資料
=================================================================
概念:
假設你的程式碼所在的程式中有多個執行緒在同一時候執行,而這些執行緒可能會同一時候執行這段程式碼。假設每次執行結果和單執行緒執行的結果是一樣的,並且其它的變數的值也和預期的是一樣的,就是執行緒安全的。
或者說:一個類或者程式所提供的介面對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
執行緒安全問題都是由全域性變數靜態變數引起的。
若每一個執行緒中對全域性變數靜態變數僅僅有讀操作,而無寫操作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同一時候執行寫操作,一般都須要考慮執行緒同步,否則的話就可能影響執行緒安全。
安全性:
比方一個 ArrayList 類,在加入一個元素的時候,它可能會有兩步來完畢:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
單執行緒執行的情況下,假設 Size = 0,加入一個元素後,此元素在位置 0,並且 Size=1;
而如果是在多執行緒情況下,比方有兩個執行緒,執行緒 A 先將元素存放在位置 0。可是此時 CPU 排程執行緒A暫停,執行緒 B 得到執行的機會。執行緒B也向此 ArrayList 加入元素,由於此時 Size 仍然等於 0 (注意哦,我們如果的是加入一個元素是要兩個步驟哦,而執行緒A只完畢了步驟1),所以執行緒B也將元素存放在位置0。然後執行緒A和執行緒B都繼續執行,都新增 Size 的值。
那好,我們來看看 ArrayList 的情況,元素實際上僅僅有一個,存放在位置 0,而 Size 卻等於 2。這就是“執行緒不安全”了。
安全性:
執行緒安全性不是一個非真即假的命題。 Vector 的方法都是同步的,而且 Vector 明白地設計為在多執行緒環境中工作。可是它的執行緒安全性是有限制的,即在某些方法之間有狀態依賴(類似地,假設在迭代過程中 Vector 被其它執行緒改動,那麼由 Vector.iterator() 返回的 iterator會丟擲ConcurrentModifiicationException)。
對於 Java 類中常見的執行緒安全性級別,沒有一種分類系統可被廣泛接受,只是重要的是在編寫類時儘量記錄下它們的執行緒安全行為。
Bloch 給出了描寫敘述五類執行緒安全性的分類方法:不可變、執行緒安全、有條件執行緒安全、執行緒相容和執行緒對立。僅僅要明白地記錄下執行緒安全特性,那麼您是否使用這樣的系統都沒關係。這樣的系統有其侷限性 -- 各類之間的界線不是百分之百地明白,並且有些情況它沒照應到 -- 可是這套系統是一個非常好的起點。這樣的分類系統的核心是呼叫者能否夠或者必須用外部同步包圍操作(或者一系列操作)。以下幾節分別描寫敘述了執行緒安全性的這五種類別。

不可變

不可變的物件一定是執行緒安全的,而且永遠也不須要額外的同步[1] 。由於一個不可變的物件僅僅要構建正確,其外部可見狀態永遠也不會改變,永遠也不會看到它處於不一致的狀態。Java 類庫中大多數基本數值類如 Integer 、 String 和 BigInteger 都是不可變的。
須要注意的是,對於Integer,該類不提供add方法,加法是使用+來直接操作。而+操作是不具執行緒安全的。這是提供原子操作類AtomicInteger的原。
執行緒安全
執行緒安全的物件具有在上面“執行緒安全”一節中描寫敘述的屬性 -- 由類的規格說明所規定的約束在物件被多個執行緒訪問時仍然有效,無論執行時環境怎樣排執行緒都不須要不論什麼額外的同步。這樣的執行緒安全性保證是非常嚴格的 -- 很多類,如 Hashtable 或者 Vector 都不能滿足這樣的嚴格的定義。


有條件的

有條件的執行緒安全類對於單獨的操作能夠是執行緒安全的,可是某些操作序列可能須要外部同步。條件執行緒安全的最常見的樣例是遍歷由 Hashtable 或者 Vector 或者返回的迭代器 -- 由這些類返回的 fail-fast 迭代器假定在迭代器進行遍歷的時候底層集合不會有變化。為了保證其它執行緒不會在遍歷的時候改變集合,進行迭代的執行緒應該確保它是獨佔性地訪問集合以實現遍歷的完整性。通常,獨佔性的訪問是由對鎖的同步保證的 -- 而且類的文件應該說明是哪個鎖(一般是物件的內部監視器(intrinsic monitor))。
如果對一個有條件執行緒安全類進行記錄,那麼您應該不僅要記錄它是有條件執行緒安全的,並且還要記錄必須防止哪些操作序列的併發訪問。使用者能夠合理地如果其它操作序列不須要不論什麼額外的同步。
執行緒相容
執行緒相容類不是執行緒安全的,可是能夠通過正確使用同步而在併發環境中安全地使用。這可能意味著用一個 synchronized 塊包圍每個方法呼叫,或者建立一個包裝器物件,當中每個方法都是同步的(就像 Collections.synchronizedList() 一樣)。也可能意味著用 synchronized 塊包圍某些操作序列。為了最大程度地利用執行緒相容類,假設全部呼叫都使用同一個塊,那麼就不應該要求呼叫者對該塊同步。這樣做會使執行緒相容的物件作為變數例項包括在其它執行緒安全的物件中,從而能夠利用其全部者物件的同步。
很多常見的類是執行緒相容的,如集合類 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 類 Connection 和 ResultSet 。

執行緒對立

執行緒對立類是那些無論是否呼叫了外部同步都不能在併發使用時安全地呈現的類。執行緒對立非常少見,當類改動靜態資料,而靜態資料會影響在其它執行緒中執行的其它類的行為,這時一般會出現執行緒對立。執行緒對立類的一個樣例是呼叫 System.setOut() 的類。



相關文章