集合是程式設計中最常用的資料結構

2021-07-27 12:59:12 字數 2723 閱讀 4821

集合是程式設計中最常用的資料結構。而談到併發,幾乎總是離不開集合這類高階資料結構的支援。比如兩個執行緒需要同時訪問乙個中間臨界區(queue),比如常會用快取作為外部檔案的副本(hashmap)。這篇文章主要分析jdk1.5的3種併發集合型別(concurrent,copyonright,queue)中的concurrenthashmap,讓我們從原理上細緻的了解它們,能夠讓我們在深度專案開發中獲益非淺。

通過分析hashtable就知道,synchronized是針對整張hash表的,即每次鎖住整張表讓執行緒獨佔,concurrenthashmap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。concurrenthashmap內部使用段(segment)來表示這些不同的部分,每個段其實就是乙個小的hash table,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。

有些方法需要跨段,比如size()和containsvalue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢後,又按順序釋放所有段的鎖。這裡「按順序」是很重要的,否則極有可能出現死鎖,在concurrenthashmap內部,段陣列是final的,並且其成員變數實際上也是final的,但是,僅僅是將陣列宣告為final的並不保證陣列成員也是final的,這需要實現上的保證。這可以確保不會出現死鎖,因為獲得鎖的順序是固定的。

一、結構解析

concurrenthashmap和hashtable主要區別就是圍繞著鎖的粒度以及如何鎖,可以簡單理解成把乙個大的hashtable分解成多個,形成了鎖分離。如圖:

而hashtable的實現方式是---鎖整個hash表

二、應用場景

當有乙個大陣列時需要在多個執行緒共享時就可以考慮是否把它給分層多個節點了,避免大鎖。並可以考慮通過hash演算法進行一些模組定位。

其實不止用於執行緒,當設計資料表的事務時(事務某種意義上也是同步機制的體現),可以把乙個表看成乙個需要同步的陣列,如果操作的表資料太多時就可以考慮事務分離了(這也是為什麼要避免大表的出現),比如把資料進行字段拆分,水平分表等.

三、原始碼解讀

concurrenthashmap中主要實體類就是三個:concurrenthashmap(整個hash表),segment(桶),hashentry(節點),對應上面的圖可以看出之間的關係

/** 

* the segments, each of which is a specialized hash table

*/

final segment segments;

不變(immutable)和易變(volatile)

concurrenthashmap完全允許多個讀操作併發進行,讀操作並不需要加鎖。如果使用傳統的技術,如hashmap中的實現,如果允許可以在hash鏈的中間新增或刪除元素,讀操作不加鎖將得到不一致的資料。concurrenthashmap實現技術是保證hashentry幾乎是不可變的。hashentry代表每個hash鏈中的乙個節點,其結構如下所示:

1. static final class hashentry
可以看到除了value不是final的,其它值都是final的,這意味著不能從hash鏈的中間或尾部新增或刪除節點,因為這需要修改next 引用值,所有的節點的修改只能從頭部開始。對於put操作,可以一律新增到hash鏈的頭部。但是對於remove操作,可能需要從中間刪除乙個節點,這就需要將要刪除節點的前面所有節點整個複製一遍,最後乙個節點指向要刪除結點的下乙個結點。這在講解刪除操作時還會詳述。為了確保讀操作能夠看到最新的值,將value設定成volatile,這避免了加鎖。

其它為了加快定位段以及段中hash槽的速度,每個段hash槽的的個數都是2^n,這使得通過位運算就可以定位段和段中hash槽的位置。當併發級別為預設值16時,也就是段的個數,hash值的高4位決定分配在哪個段中。但是我們也不要忘記《演算法導論》給我們的教訓:hash槽的的個數不應該是 2^n,這可能導致hash槽分配不均,這需要對hash值重新再hash一次。(這段似乎有點多餘了 )

這是定位段的方法:

1. final segmentsegmentfor(int hash)
資料結構

關於hash表的基礎資料結構,這裡不想做過多的**。hash表的乙個很重要方面就是如何解決hash衝突,concurrenthashmap 和hashmap使用相同的方式,都是將hash值相同的節點放在乙個hash鏈中。與hashmap不同的是,concurrenthashmap使用多個子hash表,也就是段(segment)。下面是concurrenthashmap的資料成員:

1. public class concurrenthashmapextends abstractmap2.         implements concurrentmap, serializable
所有的成員都是final的,其中segmentmask和segmentshift主要是為了定位段,參見上面的segmentfor方法。

每個segment相當於乙個子hash表,它的資料成員如下:

1.     static final class segmentextends reentrantlock implements serializable   

count用來統計該段資料的個數,它是volatile(volatile 變數使用指南

常用資料結構 集合

集合是一組用 括起來的無序不重複元素,元素之間用逗號分隔。1.用大括號將多個元素括起來,元素間用逗號分隔 s s 2.用函式 set 可將字串 列表 元組等型別的資料轉換為集合型別 s set a b c d s type s class set a set a set type a class s...

oracle資料庫中最常用的sql語句

對sql語句進行調整,往往有一項前期工作,就是定位最常用的sql 語句。oracle資料庫可以從多個方面取得sql語句。如從資料庫自身的儲存過程或者函式中取得,也可以從前台的應用程式中取得。所以,資料庫管理員必須學會如何從資料庫當前程式庫快取中對已經存在的sql語句進行取樣。了解當前使用最頻繁的sq...

mysql資料庫中最常用的幾個命令

1 create database dbname default character set utf8 collate utf8 general ci 建立資料庫的語句 2 mysql h 172.16.40.110 uroot p 回車 輸入登入的密碼 遠端登入資料庫的命令 3 mysqladmi...