在 HashMap和 ConcurrentHashMap上使用包装器类 SynchronizedMap有什么区别?
HashMap
ConcurrentHashMap
SynchronizedMap
它是否只能在迭代时修改 HashMap(ConcurrentHashMap) ?
简短的回答是:
这两个映射都是 Map接口的线程安全实现。实现 ConcurrentHashMap是为了在需要高并发性的情况下提高吞吐量。
Map
Brian Goetz 关于 ConcurrentHashMap背后的想法的 文章是一本非常好的读物,强烈推荐。
ConcurrentHashMap是线程安全的,不需要同步整个映射。在使用锁完成写操作时,读操作可能发生得非常快。
ConcurrentHashMap使用称为 lock stripping的细粒度锁定机制,以允许更大程度的共享访问。由于这一点,它提供了更好的 并发性和 可伸缩性。
lock stripping
此外,为 ConcurrentHashMap返回的迭代器是 弱的一致性,而不是 Synchronized HashMap 使用的 快速失败技巧快速失败技巧。
同步 HashMap:
每个方法都使用一个 object level lock进行同步,所以 synchMap 上的 get 和 put 方法获得一个锁。
object level lock
锁定整个集合是一个性能开销。当一个线程持有该锁时,其他线程不能使用该集合。
ConcurrentHashMap是在 JDK 5中引入的。
在对象级没有锁定,锁定的粒度要细得多。对于 ConcurrentHashMap,锁可能位于 hashmap bucket level。
hashmap bucket level
较低级别锁定的效果是,您可以拥有并发的读取器和写入器,而这对于同步集合是不可能的。这将带来更大的可伸缩性。
如果一个线程试图修改 ConcurrentModificationException,而另一个线程在其上迭代,则 ConcurrentHashMap不会抛出 ConcurrentModificationException。
ConcurrentModificationException
这篇文章 Java7: HashMap vs ConcurrentHashMap是一个非常好的阅读。强烈推荐。
SynchronizedMap上的方法持有对象上的锁,而在 ConcurrentHashMap中有一个“锁条带”的概念,其中锁被持有在内容的桶上。从而提高了可伸缩性和性能。
ConcurrentHashMap:
1)这两个映射都是 Map 接口的线程安全实现。
2)实现 ConcurrentHashMap 是为了在需要高并发性的情况下提高吞吐量。
3)在对象级别没有锁定。
同步散列映射:
1)使用对象级锁对每个方法进行同步。
两者都是 HashMap 的同步版本,在核心功能和内部结构上有所不同。
ConcurrentHashMap 由内部段组成,可以从概念上看作是独立的 HashMap。 在高并发执行中,所有这些段都可以被单独的线程锁定。 因此,多个线程可以从 ConcurrentHashMap 获取/放入键值对,而无需相互阻塞/等待。 这是为了提高吞吐量而实现的。
然而呢
SynizedMap () ,我们得到一个 HashMap 的同步版本,并以阻塞方式访问它。这意味着如果多个线程试图同时访问 synizedMap,它们将被允许以同步的方式一次获取/放入一个键值对。
我们可以通过同时使用 ConcurrentHashMap 和 chronisedHashmap 来实现线程安全。但是,如果你看看他们的架构,就会发现很多不同之处。
它将在对象级别维护锁。因此,如果您想执行任何类似 put/get 的操作,那么您必须首先获取锁。同时,不允许其他线程执行任何操作。所以一次只有一个线程可以操作它。所以这里的等待时间会增加。我们可以说,当与 ConcurrentHashMap 进行比较时,性能相对较低。
它将在段级别维护锁。它有16个段,默认情况下将并发级别维护为16。因此,一次可以有16个线程在 ConcurrentHashMap 上进行操作。而且,读操作不需要锁。因此,任意数量的线程都可以对其执行 get 操作。 如果 thread1想在段2中执行 put 操作,而 thread2想在段4中执行 put 操作,那么这里允许这样做。这意味着,16个线程可以同时在 ConcurrentHashMap 上执行 update (put/delete)操作。 所以这里的等待时间会更短,因此性能相对来说要比 synized Hashmap 好。
它将在段级别维护锁。它有16个段,默认情况下将并发级别维护为16。因此,一次可以有16个线程在 ConcurrentHashMap 上进行操作。而且,读操作不需要锁。因此,任意数量的线程都可以对其执行 get 操作。
如果 thread1想在段2中执行 put 操作,而 thread2想在段4中执行 put 操作,那么这里允许这样做。这意味着,16个线程可以同时在 ConcurrentHashMap 上执行 update (put/delete)操作。
所以这里的等待时间会更短,因此性能相对来说要比 synized Hashmap 好。
ConcurrentHashMap 允许并发访问数据。
读操作即。即使在段级别,get(Object key)也没有同步。
get(Object key)
但是写操作。remove(Object key), get(Object key)获取段级锁定。只有整个映射的一部分是锁定的,其他线程仍然可以从各个段读取值,但锁定的一个段除外。
remove(Object key), get(Object key)
另一方面,SynchronizedMap 在对象级别获取锁。所有线程应该等待当前线程,而不管操作(读/写)。
按照爪哇医生的说法
Hashtable 和 Collections.synizedMap (new HashMap ())是 但是 ConcurrentHashMap 是“并发的”。 并发集合是线程安全的,但不受单个排除锁控制。 在 ConcurrentHashMap 的特定情况下,它安全地允许 任意数量的并发读取以及可调的 “ Synchronized”类在您需要的时候非常有用 以防止通过单个锁对集合进行所有访问,请访问 可伸缩性差的代价。 在其他情况下 线程被期望访问一个公共集合“并发” 版本通常是可取的 当集合是非共享的或者可访问的时候最好 只有在拿着其他锁的时候。
Hashtable 和 Collections.synizedMap (new HashMap ())是 但是 ConcurrentHashMap 是“并发的”。
并发集合是线程安全的,但不受单个排除锁控制。
在 ConcurrentHashMap 的特定情况下,它安全地允许 任意数量的并发读取以及可调的 “ Synchronized”类在您需要的时候非常有用 以防止通过单个锁对集合进行所有访问,请访问 可伸缩性差的代价。
在其他情况下 线程被期望访问一个公共集合“并发” 版本通常是可取的 当集合是非共享的或者可访问的时候最好 只有在拿着其他锁的时候。
ConcurrentHashMap 与 Synchronized HashMap 的简单性能测试 .测试流在一个线程中调用 put,在 Map上并发地在三个线程中调用 get。正如@trshiv 所说,ConcurrentHashMap 具有更高的吞吐量和速度,因为它的读操作没有锁。结果是,当操作时间超过 10^7时,ConcurrentHashMap 比 Synchronized HashMap 快于 2x。
put
get
10^7
2x
SynchronizedMap和 ConcurrentHashMap都是线程安全类,可以在多线程应用程序中使用,它们之间的主要区别在于如何实现线程安全。
SynchronizedMap获取对整个 Map 实例的锁定,而 ConcurrentHashMap将 Map 实例划分为多个段,并对这些段进行锁定。
同步 HashMap
2. 空键或值-它将允许空作为键或值。
3. 并发修改异常-通过同步映射返回迭代器抛出并发修改异常
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); } }
同步 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++; } } }