1. 程式人生 > >Java常用併發容器總結(四)

Java常用併發容器總結(四)

ConcurrentSkipListMap

1.介紹

跳錶是一種可以用來快速查詢的資料結構,類似於平衡樹,他們都可以對元素進行快速的查詢。但一個重要的區別是:對平衡樹的插入往往可能導致平衡樹進行一次全域性的調整;而對跳錶的插入和刪除之需要的區域性資料操作即可。這樣的好處是,在高併發環境下,對平衡樹的操作需要一個全域性鎖來保證執行緒安全,但是對於跳錶則只需要部分鎖,這樣會擁有更好地效能。
就查詢的效能而言,跳錶的時間複雜度也是O(logN)。因此,在併發的資料結構中,JDK使用跳錶實現一個Map。
跳錶的本質是分層的多個連結串列,最上層的元素最少,查詢操作從最上層開始進行。
使用跳錶實現Map和使用Hash演算法實現Map的另一個不同之處在於,HashMap並不會維護元素的順序,而跳錶內的所有元素都是排序的,在對跳錶遍歷時會得到一個有序的結果

。因此,如果需要一個有序的併發安全的Map,使用跳錶是不二的選擇。

2.程式碼分析

ConcurrentSkipListMap是使用跳錶實現的併發安全的Map結構,其內部由幾個關鍵的資料結構組成。首先是Node。一個Node表示跳錶的某一層的一個節點。

 //一個Node表示跳錶的一個節點
 static final class Node<K,V> {
        //該節點的key
        final K key;

        //該節點的value
        volatile Object value;

        //資料域,指向同一層的後繼節點
        volatile
Node<K,V> next; Node(K key, Object value, Node<K,V> next) { this.key = key; this.value = value; this.next = next; }

另一個重要的資料結構是Index,它內部包裝了Node,同時增加了向下的引用和向右的引用:

 static class Index<K,V> {
        //內部封裝了一個Node
        final Node<K,V> node;

        //資料域,指向下一層的節點
final Index<K,V> down; //資料域,指向同一層的右邊的節點 volatile Index<K,V> right; Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { this.node = node; this.down = down; this.right = right; }

此外,還有一個HeadIndex的資料結構,表示連結串列頭部的第一個Index,並記錄當前在第幾層:

 //HeadIndex表示第level層的表頭Index
 static final class HeadIndex<K,V> extends Index<K,V> {
        //表示當前在第幾層
        final int level;
        HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
            super(node, down, right);
            this.level = level;
        }
    }

對於跳錶的所有操作,就是組織好這些Index之間的連線關係。

3.適用場景

如果想在高併發環境下,使用一個有序的Map,那麼ConcurrentSkipListMap將是不二的選擇。