多執行緒筆記(三)
1. 同步容器與并發容器
同步容器
通過synchronized關鍵字實作執行緒安全的容器;或通過Collections這個工具類的synchronizedXXX方法創建的容器,都稱為同步容器
例如Vector, Stack, Hashtable
Vector是list介面的執行緒安全實作
Stack是Vector的子類,是一個先進后出的堆疊,入堆疊和出堆疊都是同步的
Hashtable是Map介面的執行緒安全實作
并發容器
同步容器一次只能允許一個執行緒去使用,因此性能較差,
允許多執行緒同時使用容器,并能保證執行緒安全的容器都是并發容器,
并發容器有兩個介面,分別為ConcurrentMap和BlockingQueue
主要的實作
- CopyOnWrite容器
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- ConcurrentMap的實作類
- ConcurrentHashMap
- ConcurrentSkipListMap(支持排序)
- 阻塞佇列的實作
- ArrayBlockingQueue:使用陣列實作的有界阻塞佇列
- LinkedBlockingQueue:使用鏈表實作的有界阻塞佇列
- PriorityBlockingQueue:支持優先級的無界阻塞佇列
- DelayQueue:支持延時獲取元素的無界阻塞佇列
- SyncronousQueue:不存盤元素的阻塞佇列
- LinkedTransferQueue:使用鏈表實作的無界阻塞佇列
- LinkedBlockingDeque:使用鏈表實作的雙向阻塞佇列
- 非阻塞佇列的實作:
- ConcurrentLinkedQueue:使用鏈表實作的無界非阻塞佇列
- ConcurrentLinkedDeque:使用鏈表實作的雙向非阻塞佇列
2. CopyOnWriteArrayList
CopyOnWriteArrayList是一個允許多執行緒使用,能夠保證執行緒安全,底層使用陣列實作的并發容器,
基本設計思想:
? 內部還是使用陣列來存放資料,CopyOnWrite指的是寫時復制,一個陣列在讀的時候使用原陣列,寫的時候,假如添加一個元素進來,先copy原來的陣列,添加一個元素的位置,然后把新的元素放進來,這時記憶體里面同時存在兩個陣列,原陣列支持讀請求,新陣列支持讀請求,同時將指向原陣列的變數改為指向新陣列,
缺點:
? 每次寫的時候,都去cpoy一份資料出來,如果資料比較大的話,比較耗費記憶體
? 只能保證資料最終一致,不能保證實時一致,當資料在修改的時候,讀取到的資料是”舊“的值,
適用場景:讀多寫少,對實時性要求不是特別高,
3. ConcurrentHashMap
概述
ConcurrentHashMap是一個實作Map功能的并發容器,也可以認為是一個執行緒安全的HashMap
不同JDK版本里面的實作機制不一樣的,JDK1.8之前是陣列加鏈表,JDK1.8及其之后是陣列加鏈表/紅黑樹,
ConcurrentHashMap繼承了AbstractMap,實作了ConcurrentMap介面
AbstractMap實作了Map介面,提供了Map介面的骨干實作,如果我們自己想要實作一個Map,可以繼承AbstractMap,這樣可以最大限度的減少自己實作Map這類資料結構所需要的作業量,
ConcurrentMap主要提供了一些針對Map的原子操作
內部結構
使用Node<K, V>[](Node型別的陣列)來存放資料
Node節點型別
Node:
用來存放k-v資料的node,如果發生了哈希沖突,那么就使用鏈表法解決
TreeBin:
它是一個指向紅黑樹的代理節點,用來存放資料,樹上的節點是TreeNode,TreeNode繼承了Node節點,TreeBin的作用是方便對紅黑樹的操作(左旋,右旋,洗掉,平衡等等),TreeBin還包含了加鎖解鎖等操作,
ForwardingNode
是一種臨時節點,擴容的時候才會使用,不存盤資料,
ReservationNode
保留節點,給ConcurrentHashMap中的一些特殊方法使用,不存盤資料,只在computeIfAbsent和compute這兩個方法里面使用,
擴容和資料遷移的思路
擴容:
-
陣列擴容:創建一個新陣列,通常長度為原來的兩倍
-
資料遷移:把舊的陣列里的資料拷貝到新的陣列里面
擴容部分大家可以看看這一篇 https://blog.csdn.net/zzu_seu/article/details/106698150
4. BlockingQueue
阻塞佇列(BlockingQueue):在并發環境下,呼叫佇列的程序中,會根據情況去阻塞呼叫執行緒,實作這樣帶阻塞功能的佇列,就是阻塞佇列,
阻塞佇列是通過“鎖”?來實作的,主要用在生產者-消費者模式,用于執行緒間的資料交換和系統解耦,
阻塞佇列的作用:
- 如果執行緒向佇列插入元素,而這個時候佇列滿了,就會阻塞這個執行緒,直到佇列有空閑,
- 如果執行緒從佇列中獲取元素,而這個時候,佇列為空,就會阻塞這個執行緒,直到佇列里面有資料
BlockingQueue介面中的一些方法
操作成功回傳true, 如果操作失敗拋例外:add(E e), remove(Object o)
操作成功回傳true,操作失敗回傳false:offer(E e)
佇列滿了阻塞呼叫執行緒:put(E e), take()
阻塞+超時:offer(E e, long timeout, TimeUnit unit), poll(long timeout, TimeUnit unit)
阻塞佇列的特點:
- 不能包含null元素
- 實作這個介面的類都必須是執行緒安全的
- 可以限定容量大小
5. ArrayBlockingQueue
ArrayBlockingQueue是BlockingQueue介面的典型實作,
ArrayBlockingQueue是基于陣列來實作的,有界的阻塞佇列
ArrayBlockingQueue特點:
- 佇列容量在創建的時候指定,之后不可更改
- 插入元素在隊尾,洗掉元素在隊首
- 佇列滿了,對阻塞插入元素的執行緒,佇列為空,會阻塞洗掉元素的執行緒
- 支持公平/非公平的冊羅,默認是非公平的
- 加的鎖是全域鎖,如果在處理出隊的時候,是處理不了入隊的,反之同理,在超高并發環境下,可能會有性能問題
6. LinkedBlockingQueue
LinkedBlockingQueue是BlockingQueue介面的典型實作,
LinkedBlockingQueue是基于鏈表實作的,一種近似有界阻塞佇列,
LinkedBlockingQueue特點:
- 與ArrayBlockingQueue的全域鎖不同的是,LinkedBlockingQueue有兩把鎖,一把是控制入隊的putLock,一把是控制出隊的takeLock,
- 與ArrayBlockingQueue初始必須指定佇列大小不同的是,其可以在初始化時指定佇列的容量,如果不指定,容量大小默認為Integer的最大值,
- 與ArrayBlockingQueue可以指定公平/非公平策略不同的是,LinkedBlockingQueue不可以指定公平/非公平策略,
7. ConcurrentLinkedQueue
ConcurrentLinkedQueue是Queue介面的實作
ConcurrentLinkedQueue是基于鏈表實作的,無界的非阻塞佇列
與阻塞佇列最大的不同是,該佇列不再基于“鎖”來保證佇列的并發安全性,而是通過自旋+CAS的方式來保證
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/474626.html
標籤:Java
上一篇:【java并發編程】Lock & Condition 協調同步生產消費
下一篇:分頁查詢(思路)
