執行緒併發執行緒安全介紹及java.util.concurrent包下類介紹

zecoo發表於2016-12-30

執行緒Thread,在Java開發中多執行緒是必不可少的,但是真正能用好的並不多!

首先開啟一個執行緒三種方式

 ①new Thread(Runnable).start()

 ②thread.start();       //thread類必須繼承Thread

 ③Executor pool = Executors.newFixedThreadPool(7);pool.execute(new Runnable() ); //利用執行緒池


在多執行緒併發則一定會帶來執行緒安全的問題,如何解決執行緒安全

執行緒的安全控制有三個級別

     • JVM 級別。大多數現代處理器對併發對 某一硬體級別提供支援,通常以 compare-and-swap (CAS)指令形式。CAS 是一種低階別的、細粒度的技術,它允許多個執行緒更新一個記憶體位置,同時能夠檢測其他執行緒的衝突並進行恢復。它是許多高效能併發演算法的基礎。在 JDK 5.0 之前,Java 語言中用於協調執行緒之間的訪問的惟一原語是同步,同步是更重量級和粗粒度的。公開 CAS 可以開發高度可伸縮的併發 Java 類。


    • 低階實用程式類 -- 鎖定和原子類。使用 CAS 作為併發原語,ReentrantLock 類提供與 synchronized 原語相同的鎖定和記憶體語義,然而這樣可以更好地控制鎖定(如計時的鎖定等待、鎖定輪詢和可中斷的鎖定等待)和提供更好的可伸縮性(競爭時的高效能)。大多數開發人員將不再直接使用 ReentrantLock 類,而是使用在 ReentrantLock 類上構建的高階類。


    • 高階實用程式類。這些類實現併發構建塊,每個電腦科學課本中都會講述這些類 -- 訊號、互斥、閂鎖、屏障、交換程式、執行緒池和執行緒安全集合類等。大部分開發人員都可以在應用程式中用這些類,來替換許多同步、 wait() 和 notify() 的使用,從而提高效能、可讀性和正確性。

常見的執行緒安全操作

 ①加鎖同步 synchronized  Lock

 wait() notify()執行緒排程 已實現執行的同步

 ThreadLocal區域性變數  每一個執行緒都有一份資料

 ④Semaphore 訊號量

 ⑤volatile 保證一個變數的執行緒安全 

 等 等


接下來討論集合的多執行緒安全

原始集合框架包含三個介面:List、Map 和 Set。這三種集合是我們平常使用最多的集合,當集合遇到多執行緒時,我們必須要考慮多執行緒的問題,

比如說一個執行緒1不斷讀取集合執行緒2不斷往集合放入資料,這時就會出現問題

我們都知道vector,hashtable是在Java1.0就引入的集合,兩個都是執行緒安全的,但是現在已很少使用,原因就是內部實現的執行緒安全太消耗資源


java.util.concurrent 是什麼?

java.util.concurrent 包含許多執行緒安全、測試良好、高效能的併發構建塊。建立 java.util.concurrent 的目的就是要實現 Collection 框架對資料結構所執行的併發操作。通過提供一組可靠的、高效能併發構建塊,開發人員可以提高併發類的執行緒安全、可伸縮性、效能、可讀性和可靠性,

java.util.concurrent 中有很多執行緒安全集合、執行緒池、訊號和同步工具

要成為執行緒安全的類,在從多個執行緒訪問時,它必須繼續正確執行,而不管執行時環境執行那些執行緒的排程和交叉,且無需對部分呼叫程式碼執行任何其他同步。結果是對執行緒安全物件的操作將用於按固定的整體一致順序出現所有執行緒。


JDK 1.2 中引入的 Collection 框架是一種表示物件集合的高度靈活的框架,它使用基本介面 List、Set 和 Map。通過 JDK 提供每個集合的多次實現(HashMap、Hashtable、TreeMap、WeakHashMap、HashSet、TreeSet、 Vector、ArrayList、LinkedList 等等)。

java.util.concurrent 包新增了多個新的執行緒安全集合類(ConcurrentHashMap、CopyOnWriteArrayList 和CopyOnWriteArraySet)這些類的目的是提供高效能、高度可伸縮性、執行緒安全的基本集合型別版本

通過同步的封裝工廠(Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()),非執行緒安全集合均可表現為執行緒安全的


java.util 中的執行緒集合仍有一些缺點。例如,在迭代鎖定時,通常需要將該鎖定保留在集合中,否則,會有丟擲 ConcurrentModificationException
的危險。此外,如果從多個執行緒頻繁地訪問集合,則常常不能很好地執行這些類。

JDK 5.0 還提供了兩個新集合介面 -- Queue 和 BlockingQueue。Queue 介面與 List 類似,但它只允許從後面插入,從前面刪除。通過消除 List 的隨機

訪問要求,可以建立比現有 ArrayList 和 LinkedList 實現效能更好的 Queue 實現。因為 List 的許多應用程式實際上不需要隨機訪問,所以Queue 通常

可以替代 List,來獲得更好的效能。


ConcurrentModificationException這個問題我也遇到過,即便加上同步操作依然不能安全的併發操作,問題的根源還是在於Iterator迭代一致性的

原因


弱一致的迭代器
java.util 包中的集合類都返回 fail-fast 迭代器,這意味著它們假設執行緒在集合內容中進行迭代時,集合不會更改它的內容。如果 fail-fast 迭代器檢測到

在迭代過程中進行了更改操作,那麼它會丟擲 ConcurrentModificationException,這是不可控異常。在迭代過程中不更改集合的要求通常會對許多併發
應用程式造成不便。相反,比較好的是它允許併發修改並確保迭代器只要進行合理操作,就可以提供集合的一致檢視,如 java.util.concurrent 集合類中

的迭代器所做的那樣。java.util.concurrent 集合返回的迭代器稱為弱一致的(weakly consistent)迭代器。對於這些類,如果元素自從迭代開始已經刪
且尚未由 next() 方法返回,那麼它將不返回到呼叫者。如果元素自迭代開始已經新增,那麼它可能返回撥用者,也可能不返回。在一次迭代中,無論

何更改底層集合,元素不會被 返回兩次


可以用兩種方法建立執行緒安全支援資料的 List -- Vector 或封裝 ArrayList 和 Collections.synchronizedList()。

但是java.util.concurrent 包新增了名稱繁瑣的 CopyOnWriteArrayList。


為什麼我們想要新的執行緒安全的List類?為什麼會出現CopyOnWriteArrayList?
簡單的答案是與迭代和併發修改之間的互動有關。使用 Vector 或使用同步的 List 封裝器,返回的迭代器是 fail-fast 的,
這意味著如果在迭代過程中任何其他執行緒修改 List,迭代可能失敗。Vector 的非常普遍的應用程式是儲存通過元件註冊的監聽器的列表。當發生適合的事件時,該元件將在監聽器的列表中迭代,呼叫每個監聽器。


為了防止 ConcurrentModificationException,迭代執行緒必須複製列表或鎖定列表,以便進行整體迭代,而這兩種情況都需要大量的效能成本。CopyOnWriteArrayList 類通過每次新增或刪除元素時建立支援陣列的新副本,避免了這個問題,但是進行中的迭代保持對建立迭代器時的當前副本進行操作。雖然複製也會有一些成本,但 是在許多情況下,迭代要比修改多得多,
在這些情況下,寫入時複製要比其他備用方法具有更好的效能和併發性。



除了CopyOnWriteArrayList,還有CopyOnWriteArraySet、ConcurrentHashMap

ConcurrentLinkedQueue 快速、執行緒安全的、無阻塞 FIFO 佇列


java.util.concurrent除了這些執行緒安全的集合還有執行緒相關的類還有:

①Executor 框架 是java.util.concurrent 包中包含靈活的執行緒池實現,但是更重要的是,它包含用於管理實現 Runnable 的任務的執行的整個框架

②Future 介面允許表示已經完成的任務、正在執行過程中的任務或者尚未開始執行的任務。通過 Future 介面,可以嘗試取消尚未完成的任務,

查詢任務已經完成還是取消了,以及提取(或等待)任務的結果值。

③Semaphore、CyclicBarrier、CountdownLatch 和 Exchanger 類都是同步工具的例子。每個類都有執行緒可以呼叫的方法,

方法是否被阻塞取決於正在使用的特定同步工具的狀態和規則。


Executor 執行緒池的幾種模型

1、newFixedThreadPool建立一個指定工作執行緒數量的執行緒池。每當提交一個任務就建立一個工作執行緒,如果工作執行緒數量達到執行緒池初始的最大數

則將提交的任務存入到池佇列中。
2、newCachedThreadPool建立一個可快取的執行緒池。這種型別的執行緒池特點是: 
3、newSingleThreadExecutor建立一個單執行緒化的Executor,即只建立唯一的工作者執行緒來執行任務,如果這個執行緒異常結束,會有另一個取代它

保證順序執行(我覺得這點是它的特色)。單工作執行緒最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個執行緒是活動的 。

4、newScheduleThreadPool建立一個定長的執行緒池,而且支援定時的以及週期性的任務執行,類似於Timer。(這種執行緒池原理暫還沒完全瞭解透徹)


ps:就介紹這麼多,都是幹活

相關文章