ConcurrentHashMap 与同步 HashMap
Posted
技术标签:
【中文标题】ConcurrentHashMap 与同步 HashMap【英文标题】:ConcurrentHashMap vs Synchronized HashMap 【发布时间】:2010-11-20 11:32:51 【问题描述】:在HashMap
和ConcurrentHashMap
上使用包装类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 非常好读。强烈推荐。
【讨论】:
那么Hashtable
和Synchronized HashMap
有什么区别呢?
在 ConcurrentHashMap 和 Synchronized HashMap 之间,你推荐哪一个?
值得一提的是ConcurrentHashMap
的size()
结果可能已经过时了。根据“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】:SynchronizedMap
和ConcurrentHashMap
都是线程安全类,可以在多线程应用程序中使用,它们之间的主要区别在于它们如何实现线程安全。
【讨论】:
【参考方案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的主要内容,如果未能解决你的问题,请参考以下文章