一、前言:
JDK為我們提供了很多Map接口的實現,使得我們可以方便地處理Key-Value的數據結構。
當我們希望快速存取<Key, Value>鍵值對時我們可以使用HashMap。
當我們希望在多線程並發存取<Key, Value>鍵值對時,我們會選擇ConcurrentHashMap。
TreeMap則會幫助我們保證數據是按照Key的自然順序或者compareTo方法指定的排序規則進行排序。
OK,那么當我們需要多線程並發存取<Key, Value>數據並且希望保證數據有序時,我們需要怎么做呢?
。。。。。。
也許,我們可以選擇ConcurrentTreeMap。不好意思,JDK沒有提供這么好的數據結構給我們。
當然,我們可以自己添加lock來實現ConcurrentTreeMap,但是隨着並發量的提升,lock帶來的性能開銷也隨之增大。
Don't cry......,JDK6里面引入的ConcurrentSkipListMap也許可以滿足我們的需求。
JDK Documentation對ConcurrentSkipListMap的介紹
通過上面的介紹我們可以對ConcurrentSkipListMap中基本操作的時間復雜度有個基本的了解:
Operation | Time Complexity |
Insertion | O(log N) |
Removal | O(log N) |
Check if contains | O(log N) |
Enumerate in order | O(N) |
二、ConcurrentSkipListMap實例:
1 import java.util.Iterator; 2 import java.util.NavigableSet; 3 import java.util.concurrent.ConcurrentNavigableMap; 4 import java.util.concurrent.ConcurrentSkipListMap; 5 6 public class ConcurrentSkipListMapExample { 7 public static void main(String[] args) { 8 ConcurrentNavigableMap<String, String> concurrentSkipListMap = new ConcurrentSkipListMap<String, String>(); 9 concurrentSkipListMap.put("3", "Wednesday"); 10 concurrentSkipListMap.put("2", "Tuesday"); 11 concurrentSkipListMap.put("1", "Monday"); 12 concurrentSkipListMap.put("5", "Friday"); 13 concurrentSkipListMap.put("4", "Thursday"); 14 15 NavigableSet<String> navigableSet = concurrentSkipListMap.descendingKeySet(); 16 System.out.println("descendingKeySet: "); 17 Iterator<String> itr = navigableSet.iterator(); 18 while (itr.hasNext()) { 19 String s = itr.next(); 20 System.out.println(s); 21 } 22 System.out.println("ceilingEntry-2: " + concurrentSkipListMap.ceilingEntry("2")); 23 System.out.println("firstEntry: " + concurrentSkipListMap.firstEntry()); 24 System.out.println("lastEntry: " + concurrentSkipListMap.lastEntry()); 25 System.out.println("pollFirstEntry: " + concurrentSkipListMap.pollFirstEntry()); 26 System.out.println("now firstEntry: " + concurrentSkipListMap.firstEntry()); 27 System.out.println("pollLastEntry: " + concurrentSkipListMap.pollLastEntry()); 28 System.out.println("now lastEntry: " + concurrentSkipListMap.lastEntry()); 29 System.out.println("Entry-2: " + concurrentSkipListMap.get("2")); 30 } 31 32 }
三、ConcurrentSkipListMap性能測試:
下面,我們來比較一下ConcurrentSkipListMap與TreeMap在並發情況下查詢的性能狀況。
我們會啟動n個線程隨機讀取Map中的記錄,每個線程會讀取106次。
從測試結果,我們可以看出隨着並發度的不斷提高,ConcurrentSkipListMap相對於TreeMap的優勢也越來越明顯。
四、ConcurrentSkipListMap實現原理
skiplist數據結構介紹:
http://kenby.iteye.com/blog/1187303
concurrentskiplistmap實現並發的原理:
concurrentskiplistmap並沒有使用lock來保證線程的並發訪問和修改,而是使用了非阻塞算法來保證並發訪問(Michael-Scott 算法)
也可以參考下面的博客(http://blog.csdn.net/jy3161286/article/details/22809913)