一、集合的安全性問題java
ArrayList、HashSet、HashMap不是線程安全的算法
Vector、HashTable是線程安全的數組
區別就是源碼中各自核心方法是否添加了synchronized關鍵字安全
Collections工具類提供了相關的API,可讓上面那三個不安全的集合變爲安全的多線程
Colllections.synchronizedCollection(c)併發
Colllections.synchronizedList(list)函數
Colllections.synchronizedMap(m)工具
Colllections.synchronizedSet(s)性能
二、ArrayList內部用什麼實現的(是如何實現數組的添加和刪除的,由於數組在建立的時候長度是固定的,那麼就有個問題咱們往ArrayList中不斷的添加對象,它是如何管理這些數組呢)?優化
ArrayList內部使用Object[]實現的。接下來咱們分別分析ArrayList的構造、add、remove、clear方法的實現原理。
1、構造函數
1)空參構造
2) 帶參構造1
3)帶參構造2
若是調用構造函數的時候傳入了一個Collection的子類
①判斷該集合是否爲null,爲null則拋出空指針異常
②若是不是則將該集合轉換爲數組a。而後將數組賦值爲成員變量array,將該數組的長度做爲成員變量size。這裏面它先判斷a.getClass是否等於Object[].class(爲何要加這個判斷?),toArray方法是Collection接口定義的,所以全部的子類都有這樣的方法,list集合的toArray和Set集合的toArray返回的都是Object[]數組。
2、add方法
add方法有兩個重載,這裏只研究最簡單的那個
3、remove方法
4、clear方法
四、併發集合和普通集合如何區別?
併發集合常見的有ConcurrentMap、ConcurrentLinkedQuene、ConcurrentLinkedDeque等。併發集合位於java.util.concurrent包下,是jdk1.5以後纔有的。(concurrent:並存的,同時發生的)
在Java中有普通集合、同步(線程安全)的集合、併發集合。普通集合一般性能最高,可是不保證多線程的安全性和併發的可靠性。線程安全集合僅僅是給集合添加了synchronized同步鎖,嚴重犧牲了性能,並且對併發的效率就更低了(併發時的阻塞影響過了效率),併發集合則經過複雜的策略(分段鎖、更好的算法)不只保證了多線程的安全又提升了併發時的效率。
參考閱讀: ConcurrentHashMap是線程安全的HashMap的實現,默認構造一樣有initialCapacity和loadFactor屬性,不過還多了一個concurrentLevel屬性,三屬性默認值分別爲1六、0.7五、以及16。其內部使用鎖分段技術,維持這鎖segment的數組,在segment數組中又存放着Entity[]數組,內部hash算法將數據較均勻分佈在不一樣鎖中。
put操做:並無在此方法上加上synchronized,首先對key.hashcode進行hash操做,獲得key的hash值。hash操做的算法和map也不一樣,根據此hash值計算並獲取對應的數組中的Segment對象(繼承自ReentrantLock(可重入鎖)),接着調用此Segment對象的put方法來完成當前操做。
ConcurrentHashMap基於concurrentLevel劃分出了多個Segment來對key-value進行存儲,從而避免每次put操做都得鎖住整個數組。在默認得狀況下,最佳狀況可容許16個線程併發無阻塞得操做集合對象,儘量減小併發時得阻塞現象。
get(key):首先對key.hashCode進行hash操做,基於其值找到對應的Segment對象,調用其get方法來完成當前操做。而Segment得get操做首先經過hash值和數組大小減1的值進行按位與操做來獲取數組上對應位置的HashEntry。在這個步驟中,可能會由於對象大小的改變,以及數組上對應位置的HashEntry產生不一致性,那麼ConcurrentHashMap是如何保證的?
對象數組大小的改變只有在put操做時有可能發生,因爲HashEntry對象數組對應的變量時volatile類型的,所以能夠保證HashEntry對象數組大小發生改變,讀操做可看到最新的對象數組大小。
在獲取到了HashEntry對象後,怎麼能保證它及其next屬性構成的鏈表上的對象不會改變呢?這點ConcurrentHashMap採用了一個簡單的方式,即HashEntry對象中的hash、key、next屬性都是final的,這也就意味着沒辦法插入一個HashEntry對象到基於next屬性構成的鏈表中間或末尾。這樣就能夠保證當獲取到HashEntry對象後,其基於next屬性構建的鏈表是不會發生變化的。
ConcurrentHashMap默認狀況下將數據分爲16個段進行存儲,而且16個段分別持有各自不一樣的鎖Segment,鎖僅用於put和remove等改變集合對象的操做,基於volatile(易變的、無定性的、無常性的)(易變性變量:是說這變量可能會被意想不到的改變,這樣,編譯器就不會去假設這個變量的值了。指令關鍵字,確保本條指令不會因編譯器的優化而省略)及HashEntry鏈表的不變性實現了讀取的不加鎖。這些方式使得ConcurrentHashMap可以保持極好的併發支持,尤爲時對於讀寫比插入和刪除頻繁的Map而言,而它採用的這些方法也可謂是對於Java內存模型、併發機制深入掌握的體現。