// 添加集合中不存在的元素int addAllAbsent(Collection<? extends E> c)// 該元素若不存在則添加boolean addIfAbsent(E e)CopyOnWriteArraySet:木有新增!迭代CopyOnWriteArrayList擁有內部類:COWIterator,它是ListIterator的子類。當調用iterator函數時返回的是COWIterator對象。COWIterator不允許修改容器,你若調用則會拋出UnsupportedOperationException。優點讀操作無需加鎖,從而高效。缺點數據一致性問題由于迭代的是容器當前的快照,因此在迭代過程中容器發生的修改并不能實時被當前正在迭代的線程感知。內存占用問題由于修改容器都會復制數組,從而當數組超大時修改容器效率很低。PS:因此寫時復制容器適合存儲小容量數據。
ConcurrentHashMapjava.util包中提供了線程安全的HashTable,但這家伙只是通過簡單的同步來實現線程安全,因此效率低。只要有一條線程獲取了容器的鎖之后,其他所有的線程訪問同步函數都會被阻塞。因此同一時刻只能有一條線程訪問同步函數。而ConcurrentHashMap采用了分段鎖機制實現高效的并發訪問。分段鎖原理ConcurrentHashMap由多個Segment構成,每個Segment都包含一張哈希表。每次操作只將操作數據所屬的Segment鎖起來,從而避免將整個鎖住。數據結構ConcurrentHashMap內部包含了Segment數組,而每個Segment又繼承自ReentrantLock,因此它是一把可重入的鎖。Segment內部擁有一個HashEntry數組,它就是一張哈希表。HashEntry是單鏈表的一個節點,HashEntry數組存儲單鏈表的表頭節點。新增API
V putIfAbsent(K key, V value)
ConcurrentSkipListMap它是一個有序的Map,相當于TreeMap。TreeMap采用紅黑樹實現排序,而ConcurrentHashMap采用跳表實現有序。跳表的由來作用:存儲有序序列,并且實現高效的查找與插入刪除。存儲有序序列最簡單的辦法就是使用數組,從而查找可以采用二分搜索,但插入刪除需要移動元素較為低效。因此出現了二叉搜索樹,用來解決插入刪除移動元素的問題。但二叉搜索樹在最壞情況下會退化成一條單鏈表,搜索的效率降為O(n)。為了避免二叉搜索樹的退化,出現了二叉平衡樹,它在每次插入刪除節點后都會重新調整樹形,使得它仍然保持平衡,從而保證了搜索效率,也保證了插入刪除的效率。此外,根據平衡算法的不同,二叉平衡樹又分為:B+樹、B-樹、紅黑樹。但平衡算法過于復雜,因此出現跳表。跳表介紹跳表是條有序的單鏈表,它的每個節點都有多個指向后繼節點的引用。它有多個層次,上層都是下層的子集,從而能跳過不必要的節點,提升搜索速度。它通過空間來換取時間。如查找19的過程:
ConcurrentSkipListSet它是一個有序的、線程安全的Set,相當于線程安全的TreeSet。它內部擁有ConcurrentSkipListMap實例,本質上就是一個ConcurrentSkipListMap,只不過僅使用了Map中的key。
ArrayBlockingQueue概要ArrayBlockingQueue是一個 數組實現的 線程安全的 有限 阻塞隊列。數據結構ArrayBlockingQueue繼承自AbstractQueue,并實現了BlockingQueue接口。ArrayBlockingQueue內部由Object數組存儲元素,構造時必須要指定隊列容量。ArrayBlockingQueue由ReentrantLock實現隊列的互斥訪問,并由notEmpty、notFull這兩個Condition分別實現隊空、隊滿的阻塞。ReentrantLock分為公平鎖和非公平鎖,可以在構造ArrayBlockingQueue時指定。默認為非公平鎖。新增API
// 在隊尾添加指定元素,若隊已滿則等待指定時間boolean offer(E e, long timeout, TimeUnit unit)// 獲取并刪除隊首元素,若隊為空則阻塞等待E take()// 添加指定元素,若隊已滿則一直等待void put(E e)// 獲取隊首元素,若隊為空,則等待指定時間E poll(long timeout, TimeUnit unit)隊滿、隊空阻塞喚醒的原理隊滿阻塞:當添加元素時,若隊滿,則調用notFull.await()阻塞當前線程;當移除一個元素時調用notFull.signal()喚醒在notFull上等待的線程。隊空阻塞:當刪除元素時,若隊為空,則調用notEmpty.await()阻塞當前線程;當隊首添加元素時,調用notEmpty.signal()喚醒在notEmpty上等待的線程。
LinkedBlockingQueue概要LinkedBlockingQueue是一個 單鏈表實現的、線程安全的、無限 阻塞隊列。數據結構LinkedBlockingQueue繼承自AbstractQueue,實現了BlockingQueue接口。LinkedBlockingQueue由單鏈表實現,因此是個無限隊列。但為了方式無限膨脹,構造時可以加上容量加以限制。LinkedBlockingQueue分別采用讀取鎖和插入鎖控制讀取/刪除 和 插入過程的并發訪問,并采用notEmpty和notFull兩個Condition實現隊滿隊空的阻塞與喚醒。隊滿隊空阻塞喚醒的原理隊滿阻塞:若要插入元素,首先需要獲取putLock;在此基礎上,若此時隊滿,則調用notFull.await(),阻塞當前線程;當移除一個元素后調用notFull.signal()喚醒在notFull上等待的線程;最后,當插入操作完成后釋放putLock。隊空阻塞:若要刪除/獲取元素,首先要獲取takeLock;在此基礎上,若隊為空,則調用notEmpty.await(),阻塞當前線程;當插入一個元素后調用notEmpty.signal()喚醒在notEmpty上等待的線程;最后,當刪除操作完成后釋放takeLock。PS:API和ArrayBlockingQueue一樣。
LinkedBlockingDeque概要它是一個 由雙向鏈表實現的、線程安全的、 雙端 無限 阻塞隊列。數據結構
ConcurrentLinkedQueue概述它是一個由單鏈表實現的、線程安全的、無限 隊列。數據結構它僅僅繼承了AbstractQueue,并未實現BlockingQueue接口,因此它不是阻塞隊列,僅僅是個線程安全的普通隊列。特性head、tail、next、item均使用volatile修飾,保證其內存可見性,并未使用鎖,從而提高并發效率。PS:它究竟是怎樣在不使用鎖的情況下實現線程安全的?
新聞熱點
疑難解答