一起来聊聊 ConcurrentMap
我们来看这个经常在多线程的情况下使用的这些个容器,从Map开始讲,Map经常用的有这么几个。
ConcurrentHashMap用hash表实现的这样一个高并发容器。
既然有了ConcurrentHashMap正常情况下就应该有ConcurrentTreeMap,你可以去查查,它没有,就等于缺了一块。
为什么没有呢?原因就是ConcurrentHashMap里面用的是cas操作,这个cas操作它用在tree的时候,用在树这个节点上的时候实现起来太复杂了,所以就没有这个ConcurrentTreeMap,但是有时间也需要这样一个排好序的Map, 那就有了ConcurrentSkipListMap跳表结构就出现了。
ConcurrentSkipListMap通过跳表来实现的高并发容器并且这个Map是有排序的。
跳表是什么样的结构呢?底层本身存储的元素一个链表,它是排好顺序的,大家知道当一个链表排好顺序的时候往里插入是特别困难的,查找的时候也特别麻烦,因为你得从头去遍历查找这个元素到底在哪里。
所以就出现了这个跳表的结构,底层是一个链表,链表查找的时候比较困难怎么办,那么我们在这些链表的基础上在拿出一些关键元素来,在上面做一层,那这个关键元素的这一层也是一个链表,那这个数量特别大的话在这个基础之上在拿一层出来再做一个链表, 每层链表的数据越来越少,而且它是分层,在我们查找的时候从顶层往下开始查找。
所以呢,查找容易了很多,同时它无锁的实现难度比TreeMap又容易很多,因此在JUC里面提供了ConcurrentSkipListMap这个类。
他们两个的区别一个是有序的一个是无序的,同时都支持并发的操作。下面这个小程序是一个效率的测试其实也没多大意义,大家可以去写一下跑跑。
importjava.util.*;
importjava.util.concurrent.ConcurrentHashMap;
importjava.util.concurrent.ConcurrentSkipListMap;
importjava.util.concurrent.CountDownLatch;
publicclassT01_ConcurrentMap{
publicstaticvoidmain(String[] args){
Map map = newConcurrentHashMap<>();
//Map<String, String> map = new ConcurrentSkipListMap<>(); //¸ß²¢·¢²¢ÇÒÅÅÐò
//Map<String, String> map = new Hashtable<>();
//Map<String, String> map = new HashMap<>(); //Collections.synchronizedXXX
//TreeMap
Random r = newRandom();
Thread[] ths = newThread[100];
CountDownLatch latch = newCountDownLatch(ths.length);
longstart = System.currentTimeMillis();
for(inti=0; i
ths[i] = newThread(()->{
for(intj=0; j<10000; j++) map.put("a"+ r.nextInt(100000), "a"+ r.nextInt(100000));
latch.countDown();
});
}
Arrays.asList(ths).forEach(t->t.start());
try{
latch.await();
} catch(InterruptedException e) {
e.printStackTrace();
}
longend = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(map.size());
}
}