ConcurrentHashMap 与同步 HashMap

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

它是否只能在迭代时修改 HashMap(ConcurrentHashMap) ?

170847 次浏览

简短的回答是:

这两个映射都是 Map接口的线程安全实现。实现 ConcurrentHashMap是为了在需要高并发性的情况下提高吞吐量。

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

ConcurrentHashMap是线程安全的,不需要同步整个映射。在使用锁完成写操作时,读操作可能发生得非常快。

ConcurrentHashMap使用称为 lock stripping的细粒度锁定机制,以允许更大程度的共享访问。由于这一点,它提供了更好的 并发性可伸缩性

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

同步 HashMap:

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

  2. 锁定整个集合是一个性能开销。当一个线程持有该锁时,其他线程不能使用该集合。

ConcurrentHashMap是在 JDK 5中引入的。

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

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

  3. 如果一个线程试图修改 ConcurrentModificationException,而另一个线程在其上迭代,则 ConcurrentHashMap不会抛出 ConcurrentModificationException

这篇文章 Java7: HashMap vs ConcurrentHashMap是一个非常好的阅读。强烈推荐。

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

ConcurrentHashMap:

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

2)实现 ConcurrentHashMap 是为了在需要高并发性的情况下提高吞吐量。

3)在对象级别没有锁定。

同步散列映射:

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

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

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

然而呢

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

我们可以通过同时使用 ConcurrentHashMap 和 chronisedHashmap 来实现线程安全。但是,如果你看看他们的架构,就会发现很多不同之处。

  1. Synized 散列表

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

  1. ConcurrentHashMap

它将在段级别维护锁。它有16个段,默认情况下将并发级别维护为16。因此,一次可以有16个线程在 ConcurrentHashMap 上进行操作。而且,读操作不需要锁。因此,任意数量的线程都可以对其执行 get 操作。

如果 thread1想在段2中执行 put 操作,而 thread2想在段4中执行 put 操作,那么这里允许这样做。这意味着,16个线程可以同时在 ConcurrentHashMap 上执行 update (put/delete)操作。

所以这里的等待时间会更短,因此性能相对来说要比 synized Hashmap 好。

ConcurrentHashMap 允许并发访问数据。

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

但是写操作。remove(Object key), get(Object key)获取段级锁定。只有整个映射的一部分是锁定的,其他线程仍然可以从各个段读取值,但锁定的一个段除外。

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

按照爪哇医生的说法

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

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

在 ConcurrentHashMap 的特定情况下,它安全地允许 任意数量的并发读取以及可调的 “ Synchronized”类在您需要的时候非常有用 以防止通过单个锁对集合进行所有访问,请访问 可伸缩性差的代价。

在其他情况下 线程被期望访问一个公共集合“并发” 版本通常是可取的 当集合是非共享的或者可访问的时候最好 只有在拿着其他锁的时候。

ConcurrentHashMap 与 Synchronized HashMap 的简单性能测试 .测试流在一个线程中调用 put,在 Map上并发地在三个线程中调用 get。正如@trshiv 所说,ConcurrentHashMap 具有更高的吞吐量和速度,因为它的读操作没有锁。结果是,当操作时间超过 10^7时,ConcurrentHashMap 比 Synchronized HashMap 快于 2x

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

SynchronizedMap获取对整个 Map 实例的锁定,而 ConcurrentHashMap将 Map 实例划分为多个段,并对这些段进行锁定。

enter image description here

enter image description here

同步 HashMap

  1. 锁定机制 -它锁定整个映射,因此多个线程不能同时访问映射。因此,性能相对较低。

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

3. 并发修改异常-通过同步映射返回迭代器抛出并发修改异常

ConcurrentHashMap

1. 锁定装置-锁定部分,并发散列映射允许并发读写。所以性能相对比同步映射要好

2. 空键或值-它不允许使用 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);
}
}

enter image description here

同步 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++;
}
}


}

enter image description here