線程安全:當多個線程訪問某一個類(對象或方法)時,這個類始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。
非線程安全:非線程主要是指多個線程對同一個對象中的同一個實例變量進行操做時會出現值被更改、值不一樣步的狀況,進而影響程序的執行流程。java
按照「線程安全」的安全程度由強到弱來排序,咱們能夠將java語言中各類操做共享的數據分爲如下5類:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。數組
線程安全的實現方法:
保證線程安全以是否須要同步手段分類,分爲同步方案和無需同步方案。安全
一、互斥同步
互斥同步是最多見的一種併發正確性保障手段。同步是指在多線程併發訪問共享數據時,保證共享數據在同一時刻只被一個線程使用(同一時刻,只有一個線程在操做共享數據)。而互斥是實現同步的一種手段,臨界區、互斥量和信號量都是主要的互斥實現方式。所以,在這4個字裏面,互斥是因,同步是果;互斥是方法,同步是目的。
在java中,最基本的互斥同步手段就是synchronized關鍵字,synchronized關鍵字編譯以後,會在同步塊的先後分別造成monitorenter和monitorexit這兩個字節碼質量,這兩個字節碼指令都須要一個reference類型的參數來指明要鎖定和解鎖的對象。
此外,ReentrantLock也是經過互斥來實現同步。在基本用法上,ReentrantLock與synchronized很類似,他們都具有同樣的線程重入特性。
互斥同步最主要的問題就是進行線程阻塞和喚醒所帶來的性能問題,所以這種同步也成爲阻塞同步。從處理問題的方式上說,互斥同步屬於一種悲觀的併發策略,老是認爲只要不去作正確地同步措施(例如加鎖),那就確定會出現問題,不管共享數據是否真的會出現競爭,它都要進行加鎖。網絡
二、非阻塞同步
隨着硬件指令集的發展,出現了基於衝突檢測的樂觀併發策略,通俗地說,就是先進行操做,若是沒有其餘線程爭用共享數據,那操做就成功了;若是共享數據有爭用,產生了衝突,那就再採用其餘的補償措施。(最多見的補償錯誤就是不斷地重試,直到成功爲止),這種樂觀的併發策略的許多實現都不須要把線程掛起,所以這種同步操做稱爲非阻塞同步。
非阻塞的實現CAS(compareandswap):CAS指令須要有3個操做數,分別是內存地址(在java中理解爲變量的內存地址,用V表示)、舊的預期值(用A表示)和新值(用B表示)。CAS指令執行時,當且僅當V處的值符合舊預期值A時,處理器用B更新V處的值,不然它就不執行更新,可是不管是否更新了V處的值,都會返回V的舊值,上述的處理過程是一個原子操做。多線程
三、無需同步方案
要保證線程安全,並非必定就要進行同步,二者沒有因果關係。同步只是保證共享數據爭用時的正確性的手段,若是一個方法原本就不涉及共享數據,那它天然就無需任何同步操做去保證正確性,所以會有一些代碼天生就是線程安全的。
1)可重入代碼
可重入代碼(ReentrantCode)也稱爲純代碼(Pure Code),能夠在代碼執行的任什麼時候刻中斷它,轉而去執行另一段代碼,而在控制權返回後,原來的程序不會出現任何錯誤。全部的可重入代碼都是線程安全的,可是並不是全部的線程安全的代碼都是可重入的。
可重入代碼的特色是不依賴存儲在堆上的數據和公用的系統資源、用到的狀態量都是由參數中傳入、不調用 非可重入的方法等。
2)線程本地存儲
若是一段代碼中所需的數據必須與其餘代碼共享,那就看看這些共享數據的代碼是否能保證在同一個線程中執行?若是能保證,咱們就能夠把共享數據的可見範圍限制在同一個線程以內。這樣無需同步也能保證線程之間不出現數據的爭用問題。併發
產生死鎖的四個必要條件:性能
(1) 互斥條件:一個資源每次只能被一個進程使用。atom
(2) 請求和保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。線程
(3) 不可搶佔條件:進程已得到的資源,在末使用完以前,不能強行剝奪,只能在進程使用完時由本身釋放。對象
(4) 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不知足,就不會發生死鎖。
JDK1.5提供了java.util.concurrent.atomic包,能夠線程安全的更新一個變量。
Atomic包裏一個提供了12個類(Java8中看到17中),屬於4種類型的原子更新方式:
原子更新基本類型 3
原子更新數組 3
原子更新引用 3
原子更新屬性(字段)3
其餘 5
java8 提供的包 java.concurrent.atomic 包含了許多有用的類實現原子操做。原子操做是多個線程同時執行,確保其是安全的,且並不須要synchronized 關鍵字。這裏介紹 AtomicInteger、AtomicBoolean, AtomicLong 和 AtomicReference,這裏主要演示AtomicInteger類。
本質上,原子操做嚴重依賴於比較與交換(CAS),它是由多數現代CPU直接支持的原子指令。這些指令一般比同步塊要快。因此在只須要併發修改單個可變變量的狀況下,我建議你優先使用原子類,而不是使用鎖機制實現。