《Java併發編程藝術》Java併發容器和框架--03

1. ConcurrentHashMap

  1. HashMap擴容產生死鎖
  • 產生死鎖的原因是在多線程下,HashMap會產生死循環問題。
  1. HashTable效率太低
  • 因爲是用synchronized來約束的,所以效率低
  1. ConcurrentHashMap的鎖分段技術能有效的提高效率
    將數據分段化,沒一段都用不同的鎖來管理。就是所得粒度變大了,這樣就增大了效率,不是一杆子全打死。

1.1 ConcurrentHashMap數據結構

在這裏插入圖片描述

  • segments數組
    • ssize
    • concurrencyLevel
    • segmentMask= ssize-1
    • segmentShift
  • HashEntry

1.2 ConcurrentHashMap的操作

  1. get操作

  2. put操作

    • 是否需要擴容
    • 如何擴容
  3. size操作

1.3 對ConcurrentHashMap的自我認識

  • 首先與之比較就是HashMap和HashTable了,爲什麼不用這兩種呢?
    因爲HashMap不是不是線程安全的,因爲其在多線程擴容情況下回產生死循環的情況,容易產生死鎖。
    HashTable是線程安全的,但是其用的是重量級鎖synchronized,這個鎖的粒度很大,導致性能影響很嚴重。
    ConcurrentHashMap如其名稱,是線程安全的,其相比較於HashTable的優勢就是鎖的粒度小,採用分段加鎖的策略,性能相比較HashTable有了一定的提高。

  • ConcurrentHashMap的底層數據結構是Segment 和HashEntry.
    Segment數據結構是一個數數組,但是其下標是2的n次方,我也自是瞭解,但是對其爲何這樣設計還不是很清楚

  • ConcurrentHashMap的操作有三個
    get操作高效的原因是,其要獲取的數據是用volatile修飾的,而不需要加鎖,不加鎖性能肯定要快很多。但是爲什麼線程安全,get操作沒有寫操作,自然不存在競爭問題。

    put操作需要判斷Segment中的HashEntry是否需要擴容,然後定位到HashEnrty位置,將數據插入到對應的位置上。

    size操作有些意思,因爲大小會動態變化,最保險的做法就是加鎖統計size,但是這樣會影響性能。其採用的策略就是,執行兩次不加鎖的統計, 通過在使用一個modCount變量來記錄對集合大小影響的操作數,當兩次的modCount是相同的,就代表大小沒有變化,可以使用不加鎖統計的大小。 當不一樣時,就執行加統計大小。

2. ConcurrentLinkedQueue 用非阻塞方式實現安全隊列

組成很簡單,就是隊列和隊列節點要素。
頭節點,尾節點 item next。

關於維護尾節點 offer

這裏還比較新奇,並不是每次插入節點都要更新尾節點tail指向的值。 其用一個變量HOPS來就按當前尾節點指向節點與隊列最後最後一個節點之間的距離,當距離到達一定的界限就進行一次尾節點tail的更新。

出隊 poll()

其實也就是維護頭結點
這裏有一個問題,怎麼實現安全性,因爲多線程下,暫時考慮的頭結點在其他線程中已經出隊了,怎麼解決這種數據不一致的問題,而且還不是運用加鎖來解決這個問題。

出隊也運用了HOPS策略來維護頭結點。

其實就是判斷頭結點是否爲空,如果不爲空就使用原子操作CAS出隊,如果爲空就在定位頭結點。其實也就是運用了CAS操作,在關鍵的出隊操作弄成原子操作,這樣就不會出現數據不一致問題。

3. 阻塞隊列

有插入、移除操作

阻塞隊列常用語生產者消費者模式。阻塞隊列就是兩者交換東西的地方,這兩個不見面,有點像電影裏的你把錢放在垃圾箱裏,我等會去取,兩個不見面,只通過消息來傳遞我放了錢,我去了錢。

LinkedBlockingQueue

ArrayBlockingQueue

DelayQueue

延時獲取隊列使用的範圍還是很廣的,任何需要延時獲取的都可以放進延時隊列中,在延時隊列中根據延時的時長,時間到了就能獲取。

該隊列宗有兩個方法

  • getDelay(TimeUnit unit)

  • compareTo(Delayed o)

另個方法的排序規則要相同。

LinkedTransferQueue

將消費者要消費的資源直接傳給消費者,其效率還是很快的,該隊列並不存儲任何資源。

  • tranfer()
  • tryTranfer()
    嘗試該資源能不能直接傳給消費者,能就可以傳輸。

LinkedBlockingDeque

  • 無界雙向鏈表阻塞隊列。

阻塞隊列的實現原理

4. Fork/Join框架

的一個用於並行執行任務的框架,是一個把大任務分割成若干 個小任務,最終彙總每個小任務結果後得到大任務結果的框架。

4.1 工作竊取算法

用雙端隊列存儲任務,一個別竊取的從偷拿,竊取的從尾部拿

4.2 Fork/Join框架的設計

  • 分割任務
    ForkJoinTask
    RecursiveAction
    RecursiveTask
  • 執行任務併合並結果
    ForkJoinPool
    • ForkJoinTask
    • ForkJoinWorkerThread

Fork/Join框架的設計感悟

這個框架的任務就是將一個大的任務分割成多個小的任務,這裏就要設計分頁大小了。ForkJoinTash實現的就是分割任務,將分割的任務放在ForkJoinTash數組中。
分割主要使用的方法就是fork方法。

被分割的任務在ForkJoinPool中進通過ForkJoinWorkerThread工作線程進行執行。其中用到的方法主要是Join()和doJoin()方法。

現在只是看書,沒有實際操作,等會面遇到了實際情況,運用該框架就熟悉了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章