【发布时间】:2010-11-20 11:32:51
【问题描述】:
在HashMap 和ConcurrentHashMap 上使用包装类SynchronizedMap 有什么区别?
是否只能在迭代 HashMap 时对其进行修改 (ConcurrentHashMap)?
【问题讨论】:
在HashMap 和ConcurrentHashMap 上使用包装类SynchronizedMap 有什么区别?
是否只能在迭代 HashMap 时对其进行修改 (ConcurrentHashMap)?
【问题讨论】:
同步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 的size() 结果可能已经过时了。根据“Java Concurrency in Practice”一书,size() 允许返回近似值而不是精确计数。所以这个方法要慎用。
同步 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++;
}
}
}
【讨论】:
我们可以同时使用 ConcurrentHashMap 和 synchronisedHashmap 来实现线程安全。但是如果你看看他们的架构,就会有很大的不同。
它将保持对象级别的锁定。因此,如果您想执行 put/get 之类的任何操作,则必须先获取锁。同时,不允许其他线程执行任何操作。因此,一次只能有一个线程对此进行操作。所以这里的等待时间会增加。与 ConcurrentHashMap 相比,我们可以说性能相对较低。
它将保持段级别的锁定。它有 16 个段,默认保持并发级别为 16。所以一次可以有 16 个线程对 ConcurrentHashMap 进行操作。此外,读操作不需要锁。所以任意数量的线程都可以对其执行get操作。
如果thread1想在segment 2上执行put操作,thread2想在segment 4上执行put操作,那么这里是允许的。也就是说,16个线程一次可以对ConcurrentHashMap进行update(put/delete)操作。
这样在这里的等待时间会更少。因此性能相对优于 synchronisedHashmap。
【讨论】:
A simple performance test for ConcurrentHashMap vs Synchronized HashMap
。测试流程是在一个线程中调用put,同时在Map 的三个线程中调用get。正如@trshiv 所说,ConcurrentHashMap 对于没有锁的读取操作具有更高的吞吐量和速度。结果是当操作次数超过10^7,ConcurrentHashMap 比同步HashMap 快2x。
【讨论】:
根据 java 文档
Hashtable 和 Collections.synchronizedMap(new HashMap()) 是 同步。但是 ConcurrentHashMap 是“并发的”。
并发集合是线程安全的,但不受单个排除锁的控制。
在 ConcurrentHashMap 的特殊情况下,它安全地允许 任意数量的并发读取以及可调数量的 并发写入。 “同步”类在您需要时很有用 为了防止所有通过单个锁访问集合,在 以较差的可扩展性为代价。
在其他情况下,多个 线程应该访问一个公共集合,“并发” 版本通常是可取的。不同步的集合是 当任一集合不共享或可访问时更可取 仅当持有其他锁时。
【讨论】:
ConcurrentHashMap 允许并发访问数据。整个地图被分成几个部分。
读操作,即。 get(Object key) 即使在段级别也不同步。
但是写操作,即。 remove(Object key), get(Object key) 在段级别获取锁。整个地图只有一部分被锁定,其他线程仍然可以从除了锁定的一个段之外的各个段中读取值。
SynchronizedMap 另一方面,在对象级别获取锁。所有线程都应该等待当前线程而不管操作(读/写)。
【讨论】:
ConcurrentHashMap:
1) 两个映射都是 Map 接口的线程安全实现。
2) ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。
3) 对象级别没有锁定。
同步哈希图:
1) 每个方法都使用对象级锁进行同步。
【讨论】:
SynchronizedMap 上的方法持有对象上的锁,而在ConcurrentHashMap 中有一个“锁条带化”的概念,其中锁被持有在内容的桶上。从而提高了可扩展性和性能。
【讨论】:
简短的回答:
两个映射都是Map 接口的线程安全实现。 ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。
Brian Goetz 关于ConcurrentHashMap 背后想法的article 是一本非常好的读物。强烈推荐。
【讨论】:
Map m = Collections.synchronizedMap(new HashMap(...));docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
两者都是 HashMap 的同步版本,其核心功能和内部结构有所不同。
ConcurrentHashMap 由内部段组成,在概念上可以看作是独立的 HashMap。 所有这些段都可以在高并发执行中被单独的线程锁定。 因此,多个线程可以从 ConcurrentHashMap 获取/放置键值对,而不会相互阻塞/等待。 这是为了提高吞吐量而实现的。
而
Collections.synchronizedMap(),我们得到了一个同步版本的HashMap,它以阻塞的方式被访问。这意味着如果多个线程尝试同时访问 synchronizedMap,它们将被允许以同步的方式一次获取/放置一个键值对。
【讨论】:
ConcurrentHashMap 使用称为lock stripping 的更细粒度的锁定机制来允许更大程度的共享访问。因此,它提供了更好的并发性和可扩展性。
还为 ConcurrentHashMap 返回的迭代器是弱一致,而不是 Synchronized HashMap 使用的快速失败技术。
【讨论】:
ConcurrentHashMap 是线程安全的,无需同步整个地图。使用锁完成写入时,读取可能会非常快。
【讨论】: