`

SynchronizedMap和ConcurrentHashMap的深入分析

阅读更多
SynchronizedMap和ConcurrentHashMap的深入分析 (2010-04-03 14:13:59)
转载

标签: it 分类: 数据编程

在开始之前,先介绍下Map是什么?

javadoc中对Map的解释如下:
An object that maps keys to values . A map cannot contain duplicate keys; each key can map to at most one value.

This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.

The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.

从上可知,Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。

Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。



1. Hashtable 和 HashMap

这两个类主要有以下几方面的不同:

    Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。



    在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null 。



   这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:


Java代码

    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {  
        return new SynchronizedMap<K,V>(m);  
     } 

该方法返回的是一个SynchronizedMap 的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized 关键字进行了同步控制。



2. 潜在的线程安全问题

上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。

如下面这段代码:
Java代码

    // shm是SynchronizedMap的一个实例  
    if(shm.containsKey('key')){  
            shm.remove(key);  
    } 

这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么一个使用场景:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。



在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:
Java代码

    Set<K> keySet();  
     
    Collection<V> values();  
     
    Set<Map.Entry<K,V>> entrySet(); 

在这三个方法的基础上,我们一般通过如下方式访问Map的元素:
Java代码

    Iterator keys = map.keySet().iterator();  
     
    while(keys.hasNext()){  
            map.get(keys.next());  
    } 



在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本” 。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出 ConcurrentModificationException异常。为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过

集合类的 toArray() 方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。


3. 更好的选择:ConcurrentHashMap

java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap。ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。



上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。



在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。


PS:ConcurrentHashMap也不是万能的


分享到:
评论

相关推荐

    聊聊并发(4)深入分析ConcurrentHashMapJ

    相比`synchronized HashMap`或`Collections.synchronizedMap`包装的`HashMap`,`ConcurrentHashMap`通过分割锁和操作的粒度细化,实现了更高的并发性能。 1. **分段锁机制**:`ConcurrentHashMap`将整个哈希表分为...

    并行环境下Java哈希机制的对比及重构.zip

    Java提供了两种主要的并发哈希数据结构:ConcurrentHashMap和Collections.synchronizedMap。ConcurrentHashMap是专门为多线程设计的,它采用了分段锁策略,允许不同线程同时对不同段进行操作,大大提高了并行性能。...

    史上最全Java面试266题:算法+缓存+TCP+JVM+搜索+分布式+数据库.pdf

    6. HashMap非线程安全,线程安全的Map有Collections.synchronizedMap或ConcurrentHashMap。ConcurrentHashMap在Java 8中放弃了分段锁,改为使用CAS和锁分离技术提高并发性能。 7. 有序Map实现类有TreeMap,它通过...

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    2. **使用`Collections.synchronizedMap()`方法**:可以将普通的`HashMap`转换成线程安全的版本。 3. **读写分离**:虽然文档中没有详细解释这一策略,但可以通过使用读写锁来实现。 #### 四、使用并发环境安全的...

    java.util.concurrent系列文章(2)

    ### Java.util.concurrent 系列文章(2):深入理解 ...通过本文的学习,开发者可以更好地理解 `ConcurrentHashMap` 如何克服传统 `Hashtable` 和 `synchronizedMap` 的局限性,并在自己的项目中合理地应用它。

    并发编程以及计算机底层原理

    ConcurrentHashMap是一种线程安全的哈希映射表,相比synchronized Map,它提供了更高的并发性和更少的锁定。ConcurrentHashMap通过分段锁机制来实现并发控制,允许在不同段上的并发修改。 5. **原子操作与CAS**:`...

    java面试笔试题大汇总

    - ConcurrentHashMap与Collections.synchronizedMap的区别。 5. **内存管理与垃圾回收**: - 堆内存和栈内存:理解对象的存储位置。 - 垃圾收集机制:GC的工作原理,如何触发GC,如何分析和调优。 - 对象生命...

    Java程序性能相当的好

    合理使用并发容器,如ConcurrentHashMap,比传统的synchronized Map在多线程环境下更具优势。 最后,监控和分析工具的使用不容忽视。JVisualVM、JProfiler等工具可以帮助开发者深入了解程序运行状况,找出性能瓶颈...

    Java 并发编程

    Java提供了ConcurrentHashMap、CopyOnWriteArrayList等线程安全的集合类,以及通过Collections工具类中的synchronizedList、synchronizedMap等方法提供的包装器类来确保线程安全。 死锁与线程协作 死锁是指两个或多...

    java 自整理的基础面试知识

    装饰者模式在JDK中广泛应用,如Collections.synchronizedMap等,用于在运行时动态地给对象增加功能。 并发包源码 Java并发包(java.util.concurrent)包含了许多线程安全的类,如Semaphore、CountDownLatch、...

    java并发集合

    1. **ConcurrentHashMap**:线程安全的哈希映射表,它比synchronized的Hashtable或Collections.synchronizedMap()包装的HashMap有更高的并发性能。ConcurrentHashMap采用了分段锁(Segment)的设计,允许不同的段...

    自定义map实现java的hashmap

    - 线程安全:Java中的HashMap不是线程安全的,如果在多线程环境下使用,需要考虑同步机制,如使用`Collections.synchronizedMap()`或者使用`ConcurrentHashMap`。 - 空值处理:键或值为null的情况需要特殊处理,...

    阿里2023实习生-客户端笔试题目解析

    总的来说,阿里2023实习生客户端笔试中的Java题目可能会覆盖上述所有领域,并且可能结合实际问题进行深入分析。因此,对Java基础知识的扎实掌握,以及对客户端开发特性的理解,是成功应对笔试的关键。通过充分的复习...

    java学习内容[归类].pdf

    深入分析这些类的源码有助于理解它们的工作原理。Java NIO(非阻塞I/O)提供了更高效的数据传输。在并发处理方面,学习java.util.concurrent包,例如ConcurrentHashMap、ThreadPoolExecutor、FutureTask、Semaphore...

    Java后端技术面试汇总-副本.pdf

    此外,还提供了对ConcurrentHashMap工作原理的分析和源码级别的理解。涉及到的自定义数据结构,如简单的HashMap的实现,以及ThreadLocal原理的分析也是这一部分的重点。 3、进程和线程 在这一部分中,详细讨论了...

    HashMap资料.zip

    4. **线程安全**:HashMap本身不是线程安全的,如果在多线程环境下使用,需要使用Collections.synchronizedMap()方法或者ConcurrentHashMap。线程安全问题可能导致数据不一致或者死循环等问题。 5. **null键和值**...

    JavaHashSet和HashMap源码剖析编程开发技术

    5. **线程安全性**:HashSet和HashMap都不是线程安全的,如果在多线程环境下使用,需要通过Collections.synchronizedSet()或Collections.synchronizedMap()方法进行同步处理,或者使用ConcurrentHashMap等并发容器。...

    HashMap与HashTable的区别(含源码分析)

    如果在多线程环境中使用,需要程序员自己实现同步机制,如使用`synchronized`关键字或者`Collections.synchronizedMap()`。 2. null值处理: - `HashTable`不允许键或值为null,如果尝试插入null,会抛出`...

    HashMap类

    3. **线程安全性**:HashMap不是线程安全的,如果在多线程环境下使用,需要使用Collections.synchronizedMap()进行同步或者使用ConcurrentHashMap,后者是专门为并发设计的哈希映射表。 HashMap类的常见方法包括: ...

    JAVA面试题解惑系列

    - ConcurrentHashMap与Collections.synchronizedMap的区别。 - 泛型:了解它的作用和限制。 5. **IO与NIO** - 流的概念:字节流与字符流,输入流与输出流。 - 文件操作:读写、复制、删除等。 - NIO(New IO)...

Global site tag (gtag.js) - Google Analytics