記錄一個問題先:
AndroidS Studio打包APK時出現問題:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:lintVitalRelease'.
什么意思?任務':app:lintVitalRelease的執行失敗。
關於lint是個什么東西看官網:https://developer.android.com/studio/write/lint?hl=zh-CN
或者:https://blog.csdn.net/u011240877/article/details/54141714#commentBox
總之,這是一個保證代碼的可維護性與助力代碼優化的一個工具,可以檢查出代碼當中不規范或者結構混亂等威脅代碼質量的地方予以警告。
但是Lint的警告當中有的問題是不必要一定解決的,所以過度依賴這個工具,也可能造成代碼編寫時效率低下的問題。
所以在打包的過程當中,由於沒有解決掉lint中警告的問題,沒有辦法通過編譯進行打包。
所以可以在lint配置當中選擇忽略它的警告,例如在app下的build.gradle下android閉包中添加:
lintOptions { checkReleaseBuilds false abortOnError false }
就能成功打包了。
ConcurrentHashMap、CopyOnWriteArrayList、阻塞隊列
並發容器概覽
-
ConcurrentHashMap:線程安全的HashMap
-
CopyOnWriteArrayList:線程安全的List
-
BlockingQueue:這是一個接口,表示阻塞隊列,非常適合用於作為數據共享的通道。
-
ConcurrentLinkedQueue:高效的非阻塞並發隊列,使用鏈表實現。可以看做是一個線程安全的LinkedList。
-
ConcurrentSkipListMap:是一個Map,使用跳表的數據結構進行快速查找。(不常用)
集合類的相關歷史
-
Vector和Hashtable
這是早期JDK中線程安全的ArrayList和HashMap,並發性能差,所有方法都加上synchronized,那么意味着並發競爭大的時候性能不會太好。
-
ArrayList和HashMap
雖然這兩個類不是線程安全的,但是可以用Collections.synchronizedList(new ArrayList<E>())和Collections.synchronizedMap(new HashMap<K,V>())使之變成安全的。這種方式也是根據傳入的集合類的類型,比如是否RandomAccess類型,實現了RandomAccess接口,來返回一個對應的線程安全的SynchronizedRandomAccessList或者SynchronizedList,而在這些線程安全的list當中,使用synchronized方式並沒有比Vector和Hashtable當中的方法高明到哪兒去,只是沒有加在方法上而已。
-
ConcurrentHashMap、CopyOnWriteArrayList
這兩種就到了比較不錯的實現了,它們取代同步的HashMap和ArrayList。絕大多數並發情況下,ConcurrentHashMap和CopyOnWriteArrayList的性能都更好。除非是一個List經常被修改,那么用Collections.synchronizedList(new ArrayList<E>())會比使用CopyOnWriteArrayList性能更好,因為CopyOnWriteArrayList更適合讀多寫少的場景,每次寫入都會完整復制整個鏈表,比較耗費資源。
ConcurrentHashMap
-
Map
Map是一個接口,有這些實現:
-
HashMap
-
Hashtable(性能太低,如果不需要並發就直接使用HashMap,而如果在並發場景下就使用ConcurrentHashMap)
-
LinkedHashMap:HashMap的一個子類,會保存鍵值對的插入順序,在遍歷的時候就有用了,順序和插入順序一致。
-
TreeMap:由於實現了SortedMap接口,所以也就具有排序的功能,也可以自定義排序的規則。所以遍歷的時候也是排過序的。
常用方法:
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
Set<K> keySet();
//等等 -
-
為什么需要ConcurrentHashMap
為什么不用Collections.synchronizedMap(new HashMap<K,V>())?
為什么HashMap是線程不安全的?
-
同時put碰撞導致數據丟失
-
同時put擴容導致數據丟失(擴容之后的數組只有一個會被保存下來)
-
死循環造成的CPU100%
這個問題主要是出現JDK1.7及之前存在。
核心原因就是在多線程同時擴容的時候會造成鏈表的死循環。但是這個問題吧,都說了HashMap不支持並發了,並發場景下使用自然會出現一些問題。
-
-
HashMap分析
-
JDK1.7拉鏈法
-
JDK1.8拉鏈法升級為紅黑樹
關於紅黑樹:紅黑樹是對二叉查找樹BST的一種平衡策略,O(logN)vsO(N),會自動平衡,防止極端不平衡從而影響查找效率 的情況發生。紅黑樹的每個節點要么是紅色,要么是黑色,但是根節點永遠是黑色的;而且紅色節點不能連續(也就是,紅色節點的孩子和父親都不能是紅色);從任一節點到其子樹中每個葉子節點的路徑都有相同數量的黑色結點;所有葉結點都是黑色的。
-
HashMap關於並發的特點:
-
非線程安全
-
迭代的時候不允許修改內容
-
只讀的並發是安全的
-
如果一定要把HashMap用在並發環境下,
用Collections.synchronizedMap(new HashMap<K,V>())。
-
-
-
JDK1.7中的ConcurrentHashMap的實現分析
Java7中的ConcurrentHashMap最外層是多個segment,每個segment的底層數據結構和HashMap類似,仍然是數組和鏈表組成的拉鏈法。每個segment獨立上ReentrantLock鎖,每個segment之間互不影響,提高了並發效率。ConcurrentHashMap默認有16個segment,所以最多可以同時支持16個線程並發寫(操作分別分布在不同的segment上),這個默認值可以在初始化的時候設置為其他的值,但是一旦初始化以后,是不可以擴容的。
-
JDK1.8中的ConcurrentHashMap實現和分析
Java8中的ConcurrentHashMap是把代碼完全的重寫了,代碼量也從一千多行漲到了六千多行。
putVal流程:
-
判斷key不為空
-
計算hash值
-
根據對應位置的節點類型,來賦值,或者helpTransfer,或者增長鏈表,或者給紅黑樹增加節點。
-
檢查滿足閾值就“紅黑樹化”
-
返回oldVal。
get流程:
-
計算hash值
-
找到對應的位置,根據情況進行:
-
直接取值:
-
紅黑樹里取值
-
遍歷鏈表取值
-
返回找到的結果
源碼分析:
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
//省略。。。
//初始化,可見沒有任何內容,真正的初始化要等到putval方法中調用initTable方法
public ConcurrentHashMap() {
}
/**
* Initializes table, using the size recorded in sizeCtl.
* 使用sizeCtl中記錄的大小初始化表。
*/
//初始化 table,通過對 sizeCtl 的變量賦值來保證數組只能被初始化一次。
private final Node<K, V>[] initTable() {
Node<K, V>[] tab;
int sc;
//通過自旋保證初始化成功
while ((tab = table) == null || tab.length == 0) {
// 小於 0 代表有線程正在初始化,釋放當前 CPU 的調度權,重新發起鎖的競爭
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
// CAS 賦值保證當前只有一個線程在初始化,-1 代表當前只有一個線程能初始化
// 保證了數組的初始化的安全性
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
// 很有可能執行到這里的時候,table 已經不為空了,這里是雙重 check
if ((tab = table) == null || tab.length == 0) {
// 進行初始化
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
-