Java集合--ConcurrentMap 2022-03-15 02:04 299阅读 0赞 # Java集合–ConcurrentMap # ### 1 Map并发集合 ### #### 1.1 ConcurrentMap #### ConcurrentMap,它是一个接口,是一个能够支持并发访问的java.util.map集合; 在原有java.util.map接口基础上又新提供了4种方法,进一步扩展了原有Map的功能: public interface ConcurrentMap<K, V> extends Map<K, V> { //插入元素 V putIfAbsent(K key, V value); //移除元素 boolean remove(Object key, Object value); //替换元素 boolean replace(K key, V oldValue, V newValue); //替换元素 V replace(K key, V value); } \*\*putIfAbsent:\*\*与原有put方法不同的是,putIfAbsent方法中如果插入的key相同,则不替换原有的value值; \*\*remove:\*\*与原有remove方法不同的是,新remove方法中增加了对value的判断,如果要删除的key–value不能与Map中原有的key–value对应上,则不会删除该元素; \*\*replace(K,V,V):\*\*增加了对value值的判断,如果key–oldValue能与Map中原有的key–value对应上,才进行替换操作; \*\*replace(K,V):\*\*与上面的replace不同的是,此replace不会对Map中原有的key–value进行比较,如果key存在则直接替换; 其实,对于ConcurrentMap来说,我们更关注Map本身的操作,在并发情况下是如何实现数据安全的。在java.util.concurrent包中,ConcurrentMap的实现类主要以ConcurrentHashMap为主。接下来,我们具体来看下。 #### 1.2 ConcurrentHashMap #### ConcurrentHashMap是一个线程安全,并且是一个高效的HashMap。 但是,如果从线程安全的角度来说,HashTable已经是一个线程安全的HashMap,那推出ConcurrentHashMap的意义又是什么呢? 说起ConcurrentHashMap,就不得不先提及下HashMap在线程不安全的表现,以及HashTable的效率! * HashMap 关于HashMap的讲解,在此前的文章中已经说过了,本篇不在做过多的描述,有兴趣的朋友可以来这里看下–[HashMap][]。 在此节中,我们主要来说下,在多线程情况下HashMap的表现? HashMap中添加元素的源码:(基于JDK1.7.0\_45) public V put(K key, V value) { 。。。忽略 addEntry(hash, key, value, i); return null; } void addEntry(int hash, K key, V value, int bucketIndex) { 。。。忽略 createEntry(hash, key, value, bucketIndex); } //向链表头部插入元素:在数组的某一个角标下形成链表结构; void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; } 在多线程情况下,同时A、B两个线程走到createEntry()方法中,并且这两个线程中插入的元素hash值相同,bucketIndex值也相同,那么无论A线程先执行,还是B线程先被执行,最终都会2个元素先后向链表的头部插入,导致互相覆盖,致使其中1个线程中的数据丢失。这样就造成了HashMap的线程不安全,数据的不一致; 更要命的是,HashMap在多线程情况下还会出现死循环的可能,造成CPU占用率升高,导致系统卡死。 举个简单的例子: public class ConcurrentHashMapTest { public static void main(String[] agrs) throws InterruptedException { final HashMap<String,String> map = new HashMap<String,String>(); Thread t = new Thread(new Runnable(){ public void run(){ for(int x=0;x<10000;x++){ Thread tt = new Thread(new Runnable(){ public void run(){ map.put(UUID.randomUUID().toString(),""); } }); tt.start(); System.out.println(tt.getName()); } } }); t.start(); t.join(); } } 在上面的例子中,我们利用for循环,启动了10000个线程,每个线程都向共享变量中添加一个元素。 测试结果:通过使用JDK自带的jconsole工具,可以看到HashMap内部形成了死循环,并且主要集中在两处代码上。 ![img][] ![img][img 1] 那么,是什么原因造成了死循环? **HashMap–put()494行:**(基于JDK1.7.0\_45) public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { ------**for循环494行** Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } **HashMap–transfer()601行:**(基于JDK1.7.0\_45) void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; }-----**while循环601行** } } 通过查看代码,可以看出,死循环的产生:主要因为在遍历数组角标下的链表时,没有了为null的元素,单向链表变成了循环链表,头尾相连了。 以上两点,就是HashMap在多线程情况下的表现。 * HashTable 说完了HashMap的线程不安全,接下来说下HashTable的效率!! HashTable与HashMap的结构一致,都是哈希表实现。 与HashMap不同的是,在HashTable中,所有的方法都加上了synchronized锁,用锁来实现线程的安全性。由于synchronized锁加在了HashTable的每一个方法上,所以这个锁就是HashTable本身–this。那么,可想而知HashTable的效率是如何,安全是保证了,但是效率却损失了。 无论执行哪个方法,整个哈希表都会被锁住,只有其中一个线程执行完毕,释放所,下一个线程才会执行。无论你是调用get方法,还是put方法皆是如此; HashTable部分源码:(基于JDK1.7.0\_45) public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { public synchronized int size() { ...} public synchronized boolean isEmpty() { ...} public synchronized V get(Object key) { ...} public synchronized V put(K key, V value) { ...} } 通过上述代码,可以清晰看出,在HashTable中的主要操作方法上都加了synchronized锁以来保证线程安全。 说完了HashMap和HashTable,下面我们就重点介绍下ConcurrentHashMap,看看ConcurrentHashMap是如何来解决上述的两个问题的! ### 1.3 ConcurrentHashMap结构 ### 在说到ConcurrentHashMap源码之前,我们首先来了解下ConcurrentHashMap的整体结构,这样有利于我们快速理解源码。 不知道,大家还是否记得HashMap的整体结构呢?如果忘记的话,我们就在此进行回顾下! ![img][img 2] HashMap底层使用数组和链表,实现哈希表结构。插入的元素通过散列的形式分布到数组的各个角标下;当有重复的散列值时,便将新增的元素插入在链表头部,使其形成链表结构,依次向后排列。 **下面是,ConcurrentHashMap的结构:** ![img][img 3] 与HashMap不同的是,ConcurrentHashMap中多了一层数组结构,由Segment和HashEntry两个数组组成。其中Segment起到了加锁同步的作用,而HashEntry则起到了存储K.V键值对的作用。 在ConcurrentHashMap中,每一个ConcurrentHashMap都包含了一个Segment数组,在Segment数组中每一个Segment对象则又包含了一个HashEntry数组,而在HashEntry数组中,每一个HashEntry对象保存K-V数据的同时又形成了链表结构,此时与HashMap结构相同。 在多线程中,每一个Segment对象守护了一个HashEntry数组,当对ConcurrentHashMap中的元素修改时,在获取到对应的Segment数组角标后,都会对此Segment对象加锁,之后再去操作后面的HashEntry元素,这样每一个Segment对象下,都形成了一个小小的HashMap,在保证数据安全性的同时,又提高了同步的效率。只要不是操作同一个Segment对象的话,就不会出现线程等待的问题! [HashMap]: https://www.jianshu.com/p/a17b4717a721 [img]: /images/20220315/e52aacf2f5614665b0dd22452bc9d3cc.png [img 1]: /images/20220315/0a30968b3c7746b5ad523245734c09bc.png [img 2]: /images/20220315/cc9e99dfcedd449e97fd3d56a0299356.png [img 3]: /images/20220315/e34815cddfe646baa708af4ad545a821.png
相关 [Java] ConcurrentMap 分析和思考 预备知识:[Java HashMap and HashSet 的实现机制][Java HashMap and HashSet] 由预备知识可以知道hashmap 的存储结构为 痛定思痛。/ 2022年09月30日 03:58/ 0 赞/ 234 阅读
相关 Java8 并发之原子变量与ConcurrentMap 前言 > 点击查看原文 [原文地址][Link 1] 第一部分:[线程(Thread)与执行体(Executors)][Thread_Executors] 小灰灰/ 2022年06月12日 09:29/ 0 赞/ 305 阅读
相关 Java并发编程---并发类容器(ConcurrentMap容器) 一.背景前奏 jdk5.0以后提供了多种并发类容器来替代同步类容器从而改善性能.同步类容器的状态都是串行化的.他们虽然实现了线程安全.但是严重降低了并发性,在多线程环境时, 我不是女神ヾ/ 2022年06月02日 02:57/ 0 赞/ 383 阅读
相关 Atomic Variables and ConcurrentMap AtomicInteger `java.concurrent.atomic`包中包含了许多可以执行原子操作的类,所谓的原子操作是指在多线程并发的情况下无需使用`synch 悠悠/ 2022年05月14日 06:38/ 0 赞/ 251 阅读
相关 ConcurrentMap接口 ConcurrentMap接口 两个实现 ConcurrentHashMap ConcurrentSkipListMap 支持并发排序功能,弥补Concurr 今天药忘吃喽~/ 2022年05月14日 04:56/ 0 赞/ 328 阅读
相关 Atomic Variables and ConcurrentMap AtomicInteger The package `java.concurrent.atomic` contains many useful classes to pe 野性酷女/ 2022年05月14日 01:51/ 0 赞/ 330 阅读
相关 彻头彻尾的理解ConcurrentMap 转载:[https://blog.csdn.net/justloveyou\_/article/details/72783008][https_blog.csdn.net_ju ﹏ヽ暗。殇╰゛Y/ 2022年05月05日 12:58/ 0 赞/ 304 阅读
相关 Java集合--ConcurrentMap Java集合–ConcurrentMap 1 Map并发集合 1.1 ConcurrentMap ConcurrentMap,它是一个接口,是一个能够支持并发 我就是我/ 2022年03月15日 02:04/ 0 赞/ 300 阅读
相关 Java ConcurrentMap 接口 Java ConcurrentMap 接口 Java 集合框架的ConcurrentMap接口提供了一个线程安全的映射。也就是说,多个线程可以一次访问该映射,而不会影响映... 朱雀/ 2022年02月19日 12:04/ 0 赞/ 10941 阅读
相关 Java中的Map【四】ConcurrentMap接口 所使用的jdk版本为1.8版本,先看一下ConcurrentMap<K,V>在JDK中Map的UML类图中的位置: ![watermark_type_ZmFuZ3poZW5n 港控/mmm°/ 2021年09月20日 23:20/ 0 赞/ 473 阅读
还没有评论,来说两句吧...