摘要:一同步容器常用的一些容器例如都不是線程安全的,最簡單的將這些容器變為線程安全的方式,是給這些容器所有的方法都加上關鍵字。為了降低哈希沖突的成本,在鏈表長度超過時,將鏈表轉換為紅黑樹。
一、同步容器
常用的一些容器例如 ArrayList、HashMap、都不是線程安全的,最簡單的將這些容器變為線程安全的方式,是給這些容器所有的方法都加上 synchronized 關鍵字。
Java 的 Collections 中實現了這些同步容器:
簡單的使用如下:
Listlist = Collections.synchronizedList(new ArrayList<>()); Map map = Collections.synchronizedMap(new HashMap<>()); Set set = Collections.synchronizedSet(new HashSet<>());
同步容器雖然簡單,但是相應的效率較低,因為鎖的粒度較大。
循環遍歷同步容器
如果在遍歷同步容器的時候,組合了多個方法,這會可能會存在競態條件,仍然不是線程安全的。解決的辦法便是對容器加鎖。例如下面這樣:
public static void main(String[] args) { List二、并發容器list = Collections.synchronizedList(new ArrayList<>()); //省略添加數據的操作 String[] str = new String[list.size()]; int k = 0; synchronized (list){ Iterator iterator = list.iterator(); while (iterator.hasNext()){ str[k ++] = iterator.next(); } } }
Java 中還提供了一系列并發容器,相比于同步容器,其性能更好。并發容器共分為了四類:List、Map、Set、Queue。
1. ListList 中一個最主要的實現類是 CopyOnWriteArrayList ,CopyOnWrite,即寫時復制,這樣的好處是讀操作是無鎖的。
其實現原理是內部維護了一個數組,內部變量 array 指向了這個數組。需要寫時,并不是在原數組上操作,而是將數組復制一份,在拷貝的數組中進行寫。完成后,將 array 指向新的數組。這樣一來,讀寫之間不互斥,效率得到了很大的提升。
需要注意的是 CopyOnWriteArrayList 適用于讀多寫少的場景,并且需要接受讀寫的暫時不一致,因為在寫的時候,并行的讀操作可能并不能馬上看到寫的結果。
2. MapMap 的兩個主要實現類是 ConcurrentHashMap 和 ConcurrentSkipListMap,兩者主要的區別是:前者是無序的,后者是有序的。
在 Java 1.7 中,ConcurrentHashMap 的實現使用的是分段鎖技術,其內部主要的數據結構是 Segment 和 HashEntry,ConcurrentHashMap 包含了一個 Segment 數組,每個 Segment 又包含一個 HashEntry 數組,每個 HashEntry 是一個存儲數據的鏈表結構。
其中 Segment 繼承了 ReentrantLock,每個 Segment 都有對應的鎖,需要修改數據的時候,需要獲取這把鎖。修改不同的 Segment 數據,則完全可以并行,效率得到了提升。示意圖如下:
Java 1.8 又對 ConcurrentHashMap 做了較大的改進,放棄了分段鎖的技術。結構和 Java 1.8 中的 HashMap 類似,采用的是數組+鏈表/紅黑樹來實現。為了降低哈希沖突的成本,在鏈表長度超過 8 時,將鏈表轉換為紅黑樹。使用 CAS 和 synchronized 解決并發問題,鎖住鏈表或者紅黑樹的頭節點,只要沒有哈希沖突,則不會出現并發問題。示意圖如下:
ConcurrentSkipListMap 保證有序的主要原因是,底層使用的是跳表這種數據結構,關于跳表的介紹,你可以查看數據結構中的內容。
3. SetSet 的兩個實現是 CopyOnWriteArraySet 和 ConcurrentSkipListSet。
和前面說到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 實現的原理類似。
4. Queue隊列可以從兩方面進行分類:
單端和雙端:單端隊列指的是只能在隊首出隊,隊尾出隊,而雙端隊列指的是隊首和隊尾均可入隊和出隊。
阻塞和非阻塞:阻塞隊列指的是,當隊列滿的時候,入隊列阻塞;當隊列空的時候,出隊列阻塞。
Java 中,單端隊列使用 queue 標識,雙端隊列使用 deque 標識。
常用的實現類有:ArrayBlockingQueue、LinkedBlockingQueue、PriorityQueue。
其實現類是 ConcurrentLinkedQueue
其實現類是 LinkedBlockingDeque
其實現類是 ConcurrentLinkedDeque
使用其實都非常地簡單,就是入隊出隊之類的操作,這里不再贅述了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74635.html
摘要:同樣,用類型的變量來保存這些值也不是線程安全的。僅保證可見性,無法保證線程安全性。并且返回的結果是對象,是局部變量,并未使對象逸出,所以這里也是線程安全的。 《Java并發編程實戰》第3章原文 《Java并發編程實戰》中3.4.2 示例:使用Volatile類型來發布不可變對象 在前面的UnsafeCachingFactorizer類中,我們嘗試用兩個AtomicReferences變...
摘要:同步容器及其注意事項中的容器主要可以分為四個大類,分別是和,但并不是所有的容器都是線程安全的。并發容器及其注意事項在版本之前所謂的線程安全的容器,主要指的就是同步容器,當然因為所有方法都用來保證互斥,串行度太高了,性能太差了。 Java 并發包有很大一部分內容都是關于并發容器的,因此學習和搞懂這部分的內容很有必要。 Java 1.5 之前提供的同步容器雖然也能保證線程安全,但是性能很差...
摘要:今天主要講解的是本文力求簡單講清每個知識點,希望大家看完能有所收獲一和回顧線程安全的和我們知道是用于替代的,是線程安全的容器。使用迭代器遍歷時不需要顯示加鎖,看看與方法的實現可能就有點眉目了。 前言 只有光頭才能變強 showImg(https://segmentfault.com/img/remote/1460000016931828?w=1120&h=640); 前一陣子寫過一篇C...
摘要:下面是線程相關的熱門面試題,你可以用它來好好準備面試。線程安全問題都是由全局變量及靜態變量引起的。持有自旋鎖的線程在之前應該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網上流傳著,各種面試經驗及面試題,往往都是一大堆技術題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關線程的問題。Java語言一個重要的特點就是內置了對并發的支持,讓Java大受企業和程序員...
摘要:它能阻塞一組線程直到某個事件發生。與閉鎖的區別所有線程必須同時到達柵欄位置,才能繼續執行。閉鎖用于等待事件,而柵欄用于等待其它線程。閉鎖一旦進入終止狀態,就不能被重置,它是一次性對象,而柵欄可以重置。 同步容器。它的原理是將狀態封裝起來,并對每個公有方法都實行同步,使得每次只有1個線程能夠訪問容器的狀態。 Vector和HashTable Collections.synchroni...
閱讀 1816·2021-08-13 15:06
閱讀 3106·2021-08-05 10:02
閱讀 3378·2019-08-30 15:55
閱讀 2393·2019-08-30 13:46
閱讀 2493·2019-08-30 13:01
閱讀 1331·2019-08-29 17:17
閱讀 2830·2019-08-29 15:27
閱讀 1439·2019-08-29 11:12