溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java concurrency集合之ConcurrentSkipListMap_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

發(fā)布時(shí)間:2020-10-04 12:15:11 來(lái)源:腳本之家 閱讀:282 作者:skywang12345 欄目:編程語(yǔ)言

ConcurrentSkipListMap介紹

ConcurrentSkipListMap是線程安全的有序的哈希表,適用于高并發(fā)的場(chǎng)景。

ConcurrentSkipListMap和TreeMap,它們雖然都是有序的哈希表。但是,第一,它們的線程安全機(jī)制不同,TreeMap是非線程安全的,而ConcurrentSkipListMap是線程安全的。第二,ConcurrentSkipListMap是通過(guò)跳表實(shí)現(xiàn)的,而TreeMap是通過(guò)紅黑樹(shù)實(shí)現(xiàn)的。

關(guān)于跳表(Skip List),它是平衡樹(shù)的一種替代的數(shù)據(jù)結(jié)構(gòu),但是和紅黑樹(shù)不相同的是,跳表對(duì)于樹(shù)的平衡的實(shí)現(xiàn)是基于一種隨機(jī)化的算法的,這樣也就是說(shuō)跳表的插入和刪除的工作是比較簡(jiǎn)單的。 

ConcurrentSkipListMap原理和數(shù)據(jù)結(jié)構(gòu)

ConcurrentSkipListMap的數(shù)據(jù)結(jié)構(gòu),如下圖所示:

Java concurrency集合之ConcurrentSkipListMap_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

說(shuō)明:

先以數(shù)據(jù)“7,14,21,32,37,71,85”序列為例,來(lái)對(duì)跳表進(jìn)行簡(jiǎn)單說(shuō)明。

跳表分為許多層(level),每一層都可以看作是數(shù)據(jù)的索引,這些索引的意義就是加快跳表查找數(shù)據(jù)速度。每一層的數(shù)據(jù)都是有序的,上一層數(shù)據(jù)是下一層數(shù)據(jù)的子集,并且第一層(level 1)包含了全部的數(shù)據(jù);層次越高,跳躍性越大,包含的數(shù)據(jù)越少。
跳表包含一個(gè)表頭,它查找數(shù)據(jù)時(shí),是從上往下,從左往右進(jìn)行查找。現(xiàn)在“需要找出值為32的節(jié)點(diǎn)”為例,來(lái)對(duì)比說(shuō)明跳表和普遍的鏈表。

情況1:鏈表中查找“32”節(jié)點(diǎn)

路徑如下圖1-02所示:

Java concurrency集合之ConcurrentSkipListMap_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

需要4步(紅色部分表示路徑)。

情況2:跳表中查找“32”節(jié)點(diǎn)

路徑如下圖1-03所示:

Java concurrency集合之ConcurrentSkipListMap_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

忽略索引垂直線路上路徑的情況下,只需要2步(紅色部分表示路徑)。

下面說(shuō)說(shuō)Java中ConcurrentSkipListMap的數(shù)據(jù)結(jié)構(gòu)。
(01) ConcurrentSkipListMap繼承于AbstractMap類,也就意味著它是一個(gè)哈希表。
(02) Index是ConcurrentSkipListMap的內(nèi)部類,它與“跳表中的索引相對(duì)應(yīng)”。HeadIndex繼承于Index,ConcurrentSkipListMap中含有一個(gè)HeadIndex的對(duì)象head,head是“跳表的表頭”。
(03) Index是跳表中的索引,它包含“右索引的指針(right)”,“下索引的指針(down)”和“哈希表節(jié)點(diǎn)node”。node是Node的對(duì)象,Node也是ConcurrentSkipListMap中的內(nèi)部類。 

ConcurrentSkipListMap函數(shù)列表

// 構(gòu)造一個(gè)新的空映射,該映射按照鍵的自然順序進(jìn)行排序。
ConcurrentSkipListMap()
// 構(gòu)造一個(gè)新的空映射,該映射按照指定的比較器進(jìn)行排序。
ConcurrentSkipListMap(Comparator<? super K> comparator)
// 構(gòu)造一個(gè)新映射,該映射所包含的映射關(guān)系與給定映射包含的映射關(guān)系相同,并按照鍵的自然順序進(jìn)行排序。
ConcurrentSkipListMap(Map<? extends K,? extends V> m)
// 構(gòu)造一個(gè)新映射,該映射所包含的映射關(guān)系與指定的有序映射包含的映射關(guān)系相同,使用的順序也相同。
ConcurrentSkipListMap(SortedMap<K,? extends V> m)

// 返回與大于等于給定鍵的最小鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果不存在這樣的條目,則返回 null。
Map.Entry<K,V> ceilingEntry(K key)
// 返回大于等于給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。
K ceilingKey(K key)
// 從此映射中移除所有映射關(guān)系。
void clear()
// 返回此 ConcurrentSkipListMap 實(shí)例的淺表副本。
ConcurrentSkipListMap<K,V> clone()
// 返回對(duì)此映射中的鍵進(jìn)行排序的比較器;如果此映射使用鍵的自然順序,則返回 null。
Comparator<? super K> comparator()
// 如果此映射包含指定鍵的映射關(guān)系,則返回 true。
boolean containsKey(Object key)
// 如果此映射為指定值映射一個(gè)或多個(gè)鍵,則返回 true。
boolean containsValue(Object value)
// 返回此映射中所包含鍵的逆序 NavigableSet 視圖。
NavigableSet<K> descendingKeySet()
// 返回此映射中所包含映射關(guān)系的逆序視圖。
ConcurrentNavigableMap<K,V> descendingMap()
// 返回此映射中所包含的映射關(guān)系的 Set 視圖。
Set<Map.Entry<K,V>> entrySet()
// 比較指定對(duì)象與此映射的相等性。
boolean equals(Object o)
// 返回與此映射中的最小鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果該映射為空,則返回 null。
Map.Entry<K,V> firstEntry()
// 返回此映射中當(dāng)前第一個(gè)(最低)鍵。
K firstKey()
// 返回與小于等于給定鍵的最大鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果不存在這樣的鍵,則返回 null。
Map.Entry<K,V> floorEntry(K key)
// 返回小于等于給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。
K floorKey(K key)
// 返回指定鍵所映射到的值;如果此映射不包含該鍵的映射關(guān)系,則返回 null。
V get(Object key)
// 返回此映射的部分視圖,其鍵值嚴(yán)格小于 toKey。
ConcurrentNavigableMap<K,V> headMap(K toKey)
// 返回此映射的部分視圖,其鍵小于(或等于,如果 inclusive 為 true)toKey。
ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)
// 返回與嚴(yán)格大于給定鍵的最小鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果不存在這樣的鍵,則返回 null。
Map.Entry<K,V> higherEntry(K key)
// 返回嚴(yán)格大于給定鍵的最小鍵;如果不存在這樣的鍵,則返回 null。
K higherKey(K key)
// 如果此映射未包含鍵-值映射關(guān)系,則返回 true。
boolean isEmpty()
// 返回此映射中所包含鍵的 NavigableSet 視圖。
NavigableSet<K> keySet()
// 返回與此映射中的最大鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果該映射為空,則返回 null。
Map.Entry<K,V> lastEntry()
// 返回映射中當(dāng)前最后一個(gè)(最高)鍵。
K lastKey()
// 返回與嚴(yán)格小于給定鍵的最大鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果不存在這樣的鍵,則返回 null。
Map.Entry<K,V> lowerEntry(K key)
// 返回嚴(yán)格小于給定鍵的最大鍵;如果不存在這樣的鍵,則返回 null。
K lowerKey(K key)
// 返回此映射中所包含鍵的 NavigableSet 視圖。
NavigableSet<K> navigableKeySet()
// 移除并返回與此映射中的最小鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果該映射為空,則返回 null。
Map.Entry<K,V> pollFirstEntry()
// 移除并返回與此映射中的最大鍵關(guān)聯(lián)的鍵-值映射關(guān)系;如果該映射為空,則返回 null。
Map.Entry<K,V> pollLastEntry()
// 將指定值與此映射中的指定鍵關(guān)聯(lián)。
V put(K key, V value)
// 如果指定鍵已經(jīng)不再與某個(gè)值相關(guān)聯(lián),則將它與給定值關(guān)聯(lián)。
V putIfAbsent(K key, V value)
// 從此映射中移除指定鍵的映射關(guān)系(如果存在)。
V remove(Object key)
// 只有目前將鍵的條目映射到給定值時(shí),才移除該鍵的條目。
boolean remove(Object key, Object value)
// 只有目前將鍵的條目映射到某一值時(shí),才替換該鍵的條目。
V replace(K key, V value)
// 只有目前將鍵的條目映射到給定值時(shí),才替換該鍵的條目。
boolean replace(K key, V oldValue, V newValue)
// 返回此映射中的鍵-值映射關(guān)系數(shù)。
int size()
// 返回此映射的部分視圖,其鍵的范圍從 fromKey 到 toKey。
ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
// 返回此映射的部分視圖,其鍵值的范圍從 fromKey(包括)到 toKey(不包括)。
ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)
// 返回此映射的部分視圖,其鍵大于等于 fromKey。
ConcurrentNavigableMap<K,V> tailMap(K fromKey)
// 返回此映射的部分視圖,其鍵大于(或等于,如果 inclusive 為 true)fromKey。
ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
// 返回此映射中所包含值的 Collection 視圖。
Collection<V> values()

下面從ConcurrentSkipListMap的添加,刪除,獲取這3個(gè)方面對(duì)它進(jìn)行分析。

1. 添加

下面以put(K key, V value)為例,對(duì)ConcurrentSkipListMap的添加方法進(jìn)行說(shuō)明。

public V put(K key, V value) {
  if (value == null)
    throw new NullPointerException();
  return doPut(key, value, false);
}

實(shí)際上,put()是通過(guò)doPut()將key-value鍵值對(duì)添加到ConcurrentSkipListMap中的。

doPut()的源碼如下:

private V doPut(K kkey, V value, boolean onlyIfAbsent) {
  Comparable<? super K> key = comparable(kkey);
  for (;;) {
    // 找到key的前繼節(jié)點(diǎn)
    Node<K,V> b = findPredecessor(key);
    // 設(shè)置n為“key的前繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)”,即n應(yīng)該是“插入節(jié)點(diǎn)”的“后繼節(jié)點(diǎn)”
    Node<K,V> n = b.next;
    for (;;) {
      if (n != null) {
        Node<K,V> f = n.next;
        // 如果兩次獲得的b.next不是相同的Node,就跳轉(zhuǎn)到”外層for循環(huán)“,重新獲得b和n后再遍歷。
        if (n != b.next)
          break;
        // v是“n的值”
        Object v = n.value;
        // 當(dāng)n的值為null(意味著其它線程刪除了n);此時(shí)刪除b的下一個(gè)節(jié)點(diǎn),然后跳轉(zhuǎn)到”外層for循環(huán)“,重新獲得b和n后再遍歷。
        if (v == null) {        // n is deleted
          n.helpDelete(b, f);
          break;
        }
        // 如果其它線程刪除了b;則跳轉(zhuǎn)到”外層for循環(huán)“,重新獲得b和n后再遍歷。
        if (v == n || b.value == null) // b is deleted
          break;
        // 比較key和n.key
        int c = key.compareTo(n.key);
        if (c > 0) {
          b = n;
          n = f;
          continue;
        }
        if (c == 0) {
          if (onlyIfAbsent || n.casValue(v, value))
            return (V)v;
          else
            break; // restart if lost race to replace value
        }
        // else c < 0; fall through
      }

      // 新建節(jié)點(diǎn)(對(duì)應(yīng)是“要插入的鍵值對(duì)”)
      Node<K,V> z = new Node<K,V>(kkey, value, n);
      // 設(shè)置“b的后繼節(jié)點(diǎn)”為z
      if (!b.casNext(n, z))
        break;     // 多線程情況下,break才可能發(fā)生(其它線程對(duì)b進(jìn)行了操作)
      // 隨機(jī)獲取一個(gè)level
      // 然后在“第1層”到“第level層”的鏈表中都插入新建節(jié)點(diǎn)
      int level = randomLevel();
      if (level > 0)
        insertIndex(z, level);
      return null;
    }
  }
}

說(shuō)明:doPut() 的作用就是將鍵值對(duì)添加到“跳表”中。
要想搞清doPut(),首先要弄清楚它的主干部分 —— 我們先單純的只考慮“單線程的情況下,將key-value添加到跳表中”,即忽略“多線程相關(guān)的內(nèi)容”。它的流程如下:

第1步:找到“插入位置”。
即,找到“key的前繼節(jié)點(diǎn)(b)”和“key的后繼節(jié)點(diǎn)(n)”;key是要插入節(jié)點(diǎn)的鍵。

第2步:新建并插入節(jié)點(diǎn)。
即,新建節(jié)點(diǎn)z(key對(duì)應(yīng)的節(jié)點(diǎn)),并將新節(jié)點(diǎn)z插入到“跳表”中(設(shè)置“b的后繼節(jié)點(diǎn)為z”,“z的后繼節(jié)點(diǎn)為n”)。

第3步:更新跳表。
即,隨機(jī)獲取一個(gè)level,然后在“跳表”的第1層~第level層之間,每一層都插入節(jié)點(diǎn)z;在第level層之上就不再插入節(jié)點(diǎn)了。若level數(shù)值大于“跳表的層次”,則新建一層。

主干部分“對(duì)應(yīng)的精簡(jiǎn)后的doPut()的代碼”如下(僅供參考):

private V doPut(K kkey, V value, boolean onlyIfAbsent) {
  Comparable<? super K> key = comparable(kkey);
  for (;;) {
    // 找到key的前繼節(jié)點(diǎn)
    Node<K,V> b = findPredecessor(key);
    // 設(shè)置n為key的后繼節(jié)點(diǎn)
    Node<K,V> n = b.next;
    for (;;) {
      
      // 新建節(jié)點(diǎn)(對(duì)應(yīng)是“要被插入的鍵值對(duì)”)
      Node<K,V> z = new Node<K,V>(kkey, value, n);
      // 設(shè)置“b的后繼節(jié)點(diǎn)”為z
      b.casNext(n, z);

      // 隨機(jī)獲取一個(gè)level
      // 然后在“第1層”到“第level層”的鏈表中都插入新建節(jié)點(diǎn)
      int level = randomLevel();
      if (level > 0)
        insertIndex(z, level);
      return null;
    }
  }
}

理清主干之后,剩余的工作就相對(duì)簡(jiǎn)單了。主要是上面幾步的對(duì)應(yīng)算法的具體實(shí)現(xiàn),以及多線程相關(guān)情況的處理!

2. 刪除

下面以remove(Object key)為例,對(duì)ConcurrentSkipListMap的刪除方法進(jìn)行說(shuō)明。

public V remove(Object key) {
  return doRemove(key, null);
}

實(shí)際上,remove()是通過(guò)doRemove()將ConcurrentSkipListMap中的key對(duì)應(yīng)的鍵值對(duì)刪除的。

doRemove()的源碼如下: 

final V doRemove(Object okey, Object value) {
  Comparable<? super K> key = comparable(okey);
  for (;;) {
    // 找到“key的前繼節(jié)點(diǎn)”
    Node<K,V> b = findPredecessor(key);
    // 設(shè)置n為“b的后繼節(jié)點(diǎn)”(即若key存在于“跳表中”,n就是key對(duì)應(yīng)的節(jié)點(diǎn))
    Node<K,V> n = b.next;
    for (;;) {
      if (n == null)
        return null;
      // f是“當(dāng)前節(jié)點(diǎn)n的后繼節(jié)點(diǎn)”
      Node<K,V> f = n.next;
      // 如果兩次讀取到的“b的后繼節(jié)點(diǎn)”不同(其它線程操作了該跳表),則返回到“外層for循環(huán)”重新遍歷。
      if (n != b.next)          // inconsistent read
        break;
      // 如果“當(dāng)前節(jié)點(diǎn)n的值”變?yōu)閚ull(其它線程操作了該跳表),則返回到“外層for循環(huán)”重新遍歷。
      Object v = n.value;
      if (v == null) {          // n is deleted
        n.helpDelete(b, f);
        break;
      }
      // 如果“前繼節(jié)點(diǎn)b”被刪除(其它線程操作了該跳表),則返回到“外層for循環(huán)”重新遍歷。
      if (v == n || b.value == null)   // b is deleted
        break;
      int c = key.compareTo(n.key);
      if (c < 0)
        return null;
      if (c > 0) {
        b = n;
        n = f;
        continue;
      }

      // 以下是c=0的情況
      if (value != null && !value.equals(v))
        return null;
      // 設(shè)置“當(dāng)前節(jié)點(diǎn)n”的值為null
      if (!n.casValue(v, null))
        break;
      // 設(shè)置“b的后繼節(jié)點(diǎn)”為f
      if (!n.appendMarker(f) || !b.casNext(n, f))
        findNode(key);         // Retry via findNode
      else {
        // 清除“跳表”中每一層的key節(jié)點(diǎn)
        findPredecessor(key);      // Clean index
        // 如果“表頭的右索引為空”,則將“跳表的層次”-1。
        if (head.right == null)
          tryReduceLevel();
      }
      return (V)v;
    }
  }
}

說(shuō)明:doRemove()的作用是刪除跳表中的節(jié)點(diǎn)。
和doPut()一樣,我們重點(diǎn)看doRemove()的主干部分,了解主干部分之后,其余部分就非常容易理解了。下面是“單線程的情況下,刪除跳表中鍵值對(duì)的步驟”:

第1步:找到“被刪除節(jié)點(diǎn)的位置”。
即,找到“key的前繼節(jié)點(diǎn)(b)”,“key所對(duì)應(yīng)的節(jié)點(diǎn)(n)”,“n的后繼節(jié)點(diǎn)f”;key是要?jiǎng)h除節(jié)點(diǎn)的鍵。

第2步:刪除節(jié)點(diǎn)。
即,將“key所對(duì)應(yīng)的節(jié)點(diǎn)n”從跳表中移除 -- 將“b的后繼節(jié)點(diǎn)”設(shè)為“f”!

第3步:更新跳表。
即,遍歷跳表,刪除每一層的“key節(jié)點(diǎn)”(如果存在的話)。如果刪除“key節(jié)點(diǎn)”之后,跳表的層次需要-1;則執(zhí)行相應(yīng)的操作!

主干部分“對(duì)應(yīng)的精簡(jiǎn)后的doRemove()的代碼”如下(僅供參考): 

final V doRemove(Object okey, Object value) {
  Comparable<? super K> key = comparable(okey);
  for (;;) {
    // 找到“key的前繼節(jié)點(diǎn)”
    Node<K,V> b = findPredecessor(key);
    // 設(shè)置n為“b的后繼節(jié)點(diǎn)”(即若key存在于“跳表中”,n就是key對(duì)應(yīng)的節(jié)點(diǎn))
    Node<K,V> n = b.next;
    for (;;) {
      // f是“當(dāng)前節(jié)點(diǎn)n的后繼節(jié)點(diǎn)”
      Node<K,V> f = n.next;

      // 設(shè)置“當(dāng)前節(jié)點(diǎn)n”的值為null
      n.casValue(v, null);

      // 設(shè)置“b的后繼節(jié)點(diǎn)”為f
      b.casNext(n, f);
      // 清除“跳表”中每一層的key節(jié)點(diǎn)
      findPredecessor(key);
      // 如果“表頭的右索引為空”,則將“跳表的層次”-1。
      if (head.right == null)
        tryReduceLevel();
      return (V)v;
    }
  }
}

3. 獲取

下面以get(Object key)為例,對(duì)ConcurrentSkipListMap的獲取方法進(jìn)行說(shuō)明。

public V get(Object key) {
  return doGet(key);
}

doGet的源碼如下:

private V doGet(Object okey) {
  Comparable<? super K> key = comparable(okey);
  for (;;) {
    // 找到“key對(duì)應(yīng)的節(jié)點(diǎn)”
    Node<K,V> n = findNode(key);
    if (n == null)
      return null;
    Object v = n.value;
    if (v != null)
      return (V)v;
  }
}

說(shuō)明:doGet()是通過(guò)findNode()找到并返回節(jié)點(diǎn)的。

private Node<K,V> findNode(Comparable<? super K> key) {
  for (;;) {
    // 找到key的前繼節(jié)點(diǎn)
    Node<K,V> b = findPredecessor(key);
    // 設(shè)置n為“b的后繼節(jié)點(diǎn)”(即若key存在于“跳表中”,n就是key對(duì)應(yīng)的節(jié)點(diǎn))
    Node<K,V> n = b.next;
    for (;;) {
      // 如果“n為null”,則跳轉(zhuǎn)中不存在key對(duì)應(yīng)的節(jié)點(diǎn),直接返回null。
      if (n == null)
        return null;
      Node<K,V> f = n.next;
      // 如果兩次讀取到的“b的后繼節(jié)點(diǎn)”不同(其它線程操作了該跳表),則返回到“外層for循環(huán)”重新遍歷。
      if (n != b.next)        // inconsistent read
        break;
      Object v = n.value;
      // 如果“當(dāng)前節(jié)點(diǎn)n的值”變?yōu)閚ull(其它線程操作了該跳表),則返回到“外層for循環(huán)”重新遍歷。
      if (v == null) {        // n is deleted
        n.helpDelete(b, f);
        break;
      }
      if (v == n || b.value == null) // b is deleted
        break;
      // 若n是當(dāng)前節(jié)點(diǎn),則返回n。
      int c = key.compareTo(n.key);
      if (c == 0)
        return n;
      // 若“節(jié)點(diǎn)n的key”小于“key”,則說(shuō)明跳表中不存在key對(duì)應(yīng)的節(jié)點(diǎn),返回null
      if (c < 0)
        return null;
      // 若“節(jié)點(diǎn)n的key”大于“key”,則更新b和n,繼續(xù)查找。
      b = n;
      n = f;
    }
  }
}

說(shuō)明:findNode(key)的作用是在返回跳表中key對(duì)應(yīng)的節(jié)點(diǎn);存在則返回節(jié)點(diǎn),不存在則返回null。
先弄清函數(shù)的主干部分,即拋開(kāi)“多線程相關(guān)內(nèi)容”,單純的考慮單線程情況下,從跳表獲取節(jié)點(diǎn)的算法。

第1步:找到“被刪除節(jié)點(diǎn)的位置”。
根據(jù)findPredecessor()定位key所在的層次以及找到key的前繼節(jié)點(diǎn)(b),然后找到b的后繼節(jié)點(diǎn)n。

第2步:根據(jù)“key的前繼節(jié)點(diǎn)(b)”和“key的前繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)(n)”來(lái)定位“key對(duì)應(yīng)的節(jié)點(diǎn)”。
具體是通過(guò)比較“n的鍵值”和“key”的大小。如果相等,則n就是所要查找的鍵。 

ConcurrentSkipListMap示例

import java.util.*;
import java.util.concurrent.*;

/*
 *  ConcurrentSkipListMap是“線程安全”的哈希表,而TreeMap是非線程安全的。
 *
 *  下面是“多個(gè)線程同時(shí)操作并且遍歷map”的示例
 *  (01) 當(dāng)map是ConcurrentSkipListMap對(duì)象時(shí),程序能正常運(yùn)行。
 *  (02) 當(dāng)map是TreeMap對(duì)象時(shí),程序會(huì)產(chǎn)生ConcurrentModificationException異常。
 *
 * @author skywang
 */
public class ConcurrentSkipListMapDemo1 {

  // TODO: map是TreeMap對(duì)象時(shí),程序會(huì)出錯(cuò)。
  //private static Map<String, String> map = new TreeMap<String, String>();
  private static Map<String, String> map = new ConcurrentSkipListMap<String, String>();
  public static void main(String[] args) {
  
    // 同時(shí)啟動(dòng)兩個(gè)線程對(duì)map進(jìn)行操作!
    new MyThread("a").start();
    new MyThread("b").start();
  }

  private static void printAll() {
    String key, value;
    Iterator iter = map.entrySet().iterator();
    while(iter.hasNext()) {
      Map.Entry entry = (Map.Entry)iter.next();
      key = (String)entry.getKey();
      value = (String)entry.getValue();
      System.out.print("("+key+", "+value+"), ");
    }
    System.out.println();
  }

  private static class MyThread extends Thread {
    MyThread(String name) {
      super(name);
    }
    @Override
    public void run() {
        int i = 0;
      while (i++ < 6) {
        // “線程名” + "序號(hào)"
        String val = Thread.currentThread().getName()+i;
        map.put(val, "0");
        // 通過(guò)“Iterator”遍歷map。
        printAll();
      }
    }
  }
}

(某一次)運(yùn)行結(jié)果:

 (a1, 0), (a1, 0), (b1, 0), (b1, 0),

(a1, 0), (b1, 0), (b2, 0), 
(a1, 0), (a1, 0), (a2, 0), (a2, 0), (b1, 0), (b1, 0), (b2, 0), (b2, 0), (b3, 0), 
(b3, 0), (a1, 0), 
(a2, 0), (a3, 0), (a1, 0), (b1, 0), (a2, 0), (b2, 0), (a3, 0), (b3, 0), (b1, 0), (b4, 0), 
(b2, 0), (a1, 0), (b3, 0), (a2, 0), (b4, 0), 
(a3, 0), (a1, 0), (a4, 0), (a2, 0), (b1, 0), (a3, 0), (b2, 0), (a4, 0), (b3, 0), (b1, 0), (b4, 0), (b2, 0), (b5, 0), 
(b3, 0), (a1, 0), (b4, 0), (a2, 0), (b5, 0), 
(a3, 0), (a1, 0), (a4, 0), (a2, 0), (a5, 0), (a3, 0), (b1, 0), (a4, 0), (b2, 0), (a5, 0), (b3, 0), (b1, 0), (b4, 0), (b2, 0), (b5, 0), (b3, 0), (b6, 0), 
(b4, 0), (a1, 0), (b5, 0), (a2, 0), (b6, 0), 
(a3, 0), (a4, 0), (a5, 0), (a6, 0), (b1, 0), (b2, 0), (b3, 0), (b4, 0), (b5, 0), (b6, 0), 


結(jié)果說(shuō)明:

示例程序中,啟動(dòng)兩個(gè)線程(線程a和線程b)分別對(duì)ConcurrentSkipListMap進(jìn)行操作。以線程a而言,它會(huì)先獲取“線程名”+“序號(hào)”,然后將該字符串作為key,將“0”作為value,插入到ConcurrentSkipListMap中;接著,遍歷并輸出ConcurrentSkipListMap中的全部元素。 線程b的操作和線程a一樣,只不過(guò)線程b的名字和線程a的名字不同。

當(dāng)map是ConcurrentSkipListMap對(duì)象時(shí),程序能正常運(yùn)行。如果將map改為T(mén)reeMap時(shí),程序會(huì)產(chǎn)生ConcurrentModificationException異常。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI