ConcurrentHashMap 与同步 HashMap

Posted

技术标签:

【中文标题】ConcurrentHashMap 与同步 HashMap【英文标题】:ConcurrentHashMap vs Synchronized HashMap 【发布时间】:2010-11-20 11:32:51 【问题描述】:

HashMapConcurrentHashMap 上使用包装类SynchronizedMap 有什么区别?

是否只能在迭代 HashMap 时对其进行修改 (ConcurrentHashMap)?

【问题讨论】:

【参考方案1】:

同步HashMap

    每个方法都使用 object level lock 进行同步。所以 synchMap 上的 get 和 put 方法获取了一个锁。

    锁定整个集合是一种性能开销。当一个线程持有锁时,没有其他线程可以使用该集合。

ConcurrentHashMap 是在 JDK 5 中引入的。

    在对象级别没有锁定,锁定的粒度要细得多。对于ConcurrentHashMap,锁可能位于hashmap bucket level

    较低级别锁定的效果是您可以拥有并发的读取器和写入器,这对于同步集合是不可能的。这会带来更大的可扩展性。

    ConcurrentHashMap 不会抛出 ConcurrentModificationException,如果一个线程尝试修改它,而另一个线程正在对其进行迭代。

这篇文章Java 7: HashMap vs ConcurrentHashMap 非常好读。强烈推荐。

【讨论】:

那么HashtableSynchronized HashMap有什么区别呢? 在 ConcurrentHashMap 和 Synchronized HashMap 之间,你推荐哪一个? 值得一提的是ConcurrentHashMapsize() 结果可能已经过时了。根据“Java Concurrency in Practice”一书,size() 允许返回近似值而不是精确计数。所以这个方法要慎用。 @roottraveller 用于 Hashtable 和同步 HashMap ***.com/questions/8875680/…【参考方案2】:

同步 HashMap

    锁定机制 - 它锁定整个地图,因此多个线程不能同时访问地图。因此,性能相对较差。

2.Null 键或值 - 它将允许 null 作为键或值。

3.并发修改异常 - 同步map返回的迭代器抛出并发修改异常

ConcurrentHashMap

1.锁定机制 - 锁定部分,Concurrent hashmap允许并发读写。所以性能相对比同步地图要好

2.Null 键或值 - 它不允许 null 作为键或值。如果使用它将在运行时抛出 java.lang.NullPointerException

3.并发修改异常 - 不抛出并发修改异常。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Ex_ConcurrentHashMap 

    public static void main(String[] args) 
        
        Map<String, String> map = new ConcurrentHashMap<>();
        map.put("one", "one");
        map.put("two", "two");
        map.put("three", "three");
        System.out.println("1st  map : "+map);
        String key = null;
        for(Map.Entry<String, String> itr : map.entrySet())
        
            key = itr.getKey();
            if("three".equals(key))
            
                map.put("FOUR", "FOUR");
            
            System.out.println(key+" ::: "+itr.getValue());
        
        System.out.println("2nd  map : "+map);
        //map.put("FIVE", null);//java.lang.NullPointerException
        map.put(null, "FIVE");//java.lang.NullPointerException
        System.out.println("3rd  map : "+map);
    

同步 HashMap 示例

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class Ex_Synchronizedmap 

    public static void main(String[] args) 
        
        Map<String, String> map = new HashMap<>();
        
        map.put("one", "one");
        map.put("two", "two");
        map.put("three", "three");
        map.put("FOUR", null);
        map.put(null, "FIVE");
        
        System.out.println("map : "+map);
        
        Map<String, String> map1 = 
                Collections.synchronizedMap(map);
        System.out.println("map1 : "+map1);
        
        String key = null;
        for(Map.Entry<String, String> itr : map1.entrySet())
        
            key = itr.getKey();
            if("three".equals(key))
            
                map1.put("ABC", "ABC");
            
            System.out.println(key+" ::: "+itr.getValue());
        
        
        System.out.println("New Map :: "+map1);
        
        
        
        Iterator<Entry<String, String>> iterator = map1.entrySet().iterator();
        int i = 0;
        while(iterator.hasNext())
        
            if(i == 1)
            
                map1.put("XYZ", "XYZ");
            
            Entry<String, String> next = iterator.next();
            System.out.println(next.getKey()+" :: "+next.getValue());
            i++;
        
    


【讨论】:

【参考方案3】:

SynchronizedMapConcurrentHashMap 都是线程安全类,可以在多线程应用程序中使用,它们之间的主要区别在于它们如何实现线程安全。

【讨论】:

【参考方案4】:

我们可以同时使用 ConcurrentHashMap 和 synchronisedHashmap 来实现线程安全。但是如果你看看他们的架构,就会有很大的不同。

    同步哈希图

它将保持对象级别的锁定。因此,如果您想执行 put/get 之类的任何操作,则必须先获取锁。同时,不允许其他线程执行任何操作。因此,一次只能有一个线程对此进行操作。所以这里的等待时间会增加。与 ConcurrentHashMap 相比,我们可以说性能相对较低。

    ConcurrentHashMap

它将保持段级别的锁定。它有 16 个段,默认保持并发级别为 16。所以一次可以有 16 个线程对 ConcurrentHashMap 进行操作。此外,读操作不需要锁。所以任意数量的线程都可以对其执行get操作。

如果thread1想在segment 2上执行put操作,thread2想在segment 4上执行put操作,那么这里是允许的。也就是说,16个线程一次可以对ConcurrentHashMap进行update(put/delete)操作。

这样在这里的等待时间会更少。因此性能相对优于 synchronisedHashmap。

【讨论】:

非常好的解释非常感谢【参考方案5】:

A simple performance test for ConcurrentHashMap vs Synchronized HashMap 。测试流程是在一个线程中调用put,同时在Map 的三个线程中调用get。正如@trshiv 所说,ConcurrentHashMap 对于没有锁的读取操作具有更高的吞吐量和速度。结果是当操作次数超过10^7,ConcurrentHashMap 比同步HashMap 快2x

【讨论】:

【参考方案6】:

根据 java 文档

Hashtable 和 Collections.synchronizedMap(new HashMap()) 是 同步。但是 ConcurrentHashMap 是“并发的”。

并发集合是线程安全的,但不受单个排除锁的控制。

在 ConcurrentHashMap 的特殊情况下,它安全地允许 任意数量的并发读取以及可调数量的 并发写入。 “同步”类在您需要时很有用 为了防止所有通过单个锁访问集合,在 以较差的可扩展性为代价。

在其他情况下,多个 线程应该访问一个公共集合,“并发” 版本通常是可取的。不同步的集合是 当任一集合不共享或可访问时更可取 仅当持有其他锁时。

【讨论】:

【参考方案7】:

ConcurrentHashMap 允许并发访问数据。整个地图被分成几个部分。

读操作,即。 get(Object key) 即使在段级别也不同步。

但是写操作,即。 remove(Object key), get(Object key) 在段级别获取锁。整个地图只有一部分被锁定,其他线程仍然可以从除了锁定的一个段之外的各个段中读取值。

SynchronizedMap 另一方面,在对象级别获取锁。所有线程都应该等待当前线程而不管操作(读/写)。

【讨论】:

【参考方案8】:

ConcurrentHashMap:

1) 两个映射都是 Map 接口的线程安全实现。

2) ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。

3) 对象级别没有锁定。

同步哈希图:

1) 每个方法都使用对象级锁进行同步。

【讨论】:

【参考方案9】:

SynchronizedMap 上的方法持有对象上的锁,而在ConcurrentHashMap 中有一个“锁条带化”的概念,其中锁被持有在内容的桶上。从而提高了可扩展性和性能。

【讨论】:

【参考方案10】:

简短的回答:

两个映射都是Map 接口的线程安全实现。 ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。

Brian Goetz 关于ConcurrentHashMap 背后想法的article 是一本非常好的读物。强烈推荐。

【讨论】:

那是什么? HashMap:注意这个实现是不同步的,防止意外不同步访问map:Map m = Collections.synchronizedMap(new HashMap(...));docs.oracle.com/javase/7/docs/api/java/util/HashMap.html “Brian Goetz 的文章……非常好读。” - 他的《Java Concurrency in Practice》一书更是如此。【参考方案11】:

两者都是 HashMap 的同步版本,其核心功能和内部结构有所不同。

ConcurrentHashMap 由内部段组成,在概念上可以看作是独立的 HashMap。 所有这些段都可以在高并发执行中被单独的线程锁定。 因此,多个线程可以从 ConcurrentHashMap 获取/放置键值对,而不会相互阻塞/等待。 这是为了提高吞吐量而实现的。

Collections.synchronizedMap(),我们得到了一个同步版本的HashMap,它以阻塞的方式被访问。这意味着如果多个线程尝试同时访问 synchronizedMap,它们将被允许以同步的方式一次获取/放置一个键值对。

【讨论】:

【参考方案12】:

ConcurrentHashMap 使用称为lock stripping 的更细粒度的锁定机制来允许更大程度的共享访问。因此,它提供了更好的并发性可扩展性

还为 ConcurrentHashMap 返回的迭代器是弱一致,而不是 Synchronized HashMap 使用的快速失败技术

【讨论】:

【参考方案13】:

ConcurrentHashMap 是线程安全的,无需同步整个地图。使用锁完成写入时,读取可能会非常快。

【讨论】:

以上是关于ConcurrentHashMap 与同步 HashMap的主要内容,如果未能解决你的问题,请参考以下文章

并发编程__ConcurrentHashMap

这一文道尽JUC的ConcurrentHashMap

这一文道尽JUC的ConcurrentHashMap

并发容器之ConcurrentHashMap

ConcurrentHashMap底层实现

ConcurrentHashmap在我的实现中是否需要同步?