ConcurrentHashMap使用方法
来源:互联网 发布:冰冰办公软件 编辑:程序博客网 时间:2024/04/28 10:07
ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。在这之前我对ConcurrentHashMap只有一些肤浅的理解,
仅知道它采用了多个锁,大概也足够了。但是在经过一次惨痛的面试经历之后,我觉得必须深入研究它的实现。面试中被问到读是否要加锁,
因为读写会发生冲突,我说必须要加锁,我和面试官也因此发生了冲突,结果可想而知。还是闲话少说,通过仔细阅读源代码,
现在总算理解ConcurrentHashMap实现机制了,其实现之精巧,令人叹服,与大家共享之。
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,
操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,
并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,
因为获得锁的顺序是固定的。不变性是多线程编程占有很重要的地位,下面还要谈到。
Java代码
/** * The segments, each of which is a specialized hash table */ final Segment<K,V>[] segments;
不变(Immutable)和易变(Volatile)
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,
如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。
ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。
先看看代码吧,模拟1000个并发,每个测试1000次操作,循环测试10轮。分别测试Put和Get操作
运行结果:
Put time HashMapSync=3966ms.
Put time ConcurrentHashMap=1892ms.
Put time Hashtable=3892ms.
Get time HashMapSync=3812ms.
Get time ConcurrentHashMap=1828ms.
Get time Hashtable=3985ms.
仅知道它采用了多个锁,大概也足够了。但是在经过一次惨痛的面试经历之后,我觉得必须深入研究它的实现。面试中被问到读是否要加锁,
因为读写会发生冲突,我说必须要加锁,我和面试官也因此发生了冲突,结果可想而知。还是闲话少说,通过仔细阅读源代码,
现在总算理解ConcurrentHashMap实现机制了,其实现之精巧,令人叹服,与大家共享之。
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,
操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,
并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,
因为获得锁的顺序是固定的。不变性是多线程编程占有很重要的地位,下面还要谈到。
Java代码
/** * The segments, each of which is a specialized hash table */ final Segment<K,V>[] segments;
不变(Immutable)和易变(Volatile)
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,
如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。
ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。
先看看代码吧,模拟1000个并发,每个测试1000次操作,循环测试10轮。分别测试Put和Get操作
import java.util.Collections;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/** * 测试HashMap和ConcurrentHashMap的并发性能差别。 * * @author 老紫竹 JAVA世纪网(java2000.net) * */public class T { static final int threads = 1000; static final int NUMBER = 1000; public static void main(String[] args) throws Exception { Map<String, Integer> hashmapSync = Collections .synchronizedMap(new HashMap<String, Integer>()); Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<String, Integer>(); Map<String, Integer> hashtable = new Hashtable<String, Integer>(); long totalA = 0; long totalB = 0; long totalC = 0; for (int i = 0; i <= 10; i++) { totalA += testPut(hashmapSync); totalB += testPut(concurrentHashMap); totalC += testPut(hashtable); } System.out.println("Put time HashMapSync=" + totalA + "ms."); System.out.println("Put time ConcurrentHashMap=" + totalB + "ms."); System.out.println("Put time Hashtable=" + totalC + "ms."); totalA = 0; totalB = 0; totalC = 0; for (int i = 0; i <= 10; i++) { totalA += testGet(hashmapSync); totalB += testGet(concurrentHashMap); totalC += testGet(hashtable); } System.out.println("Get time HashMapSync=" + totalA + "ms."); System.out.println("Get time ConcurrentHashMap=" + totalB + "ms."); System.out.println("Get time Hashtable=" + totalC + "ms."); } public static long testPut(Map<String, Integer> map) throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < threads; i++) { new MapPutThread(map).start(); } while (MapPutThread.counter > 0) { Thread.sleep(1); } return System.currentTimeMillis() - start; } public static long testGet(Map<String, Integer> map) throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < threads; i++) { new MapPutThread(map).start(); } while (MapPutThread.counter > 0) { Thread.sleep(1); } return System.currentTimeMillis() - start; }}class MapPutThread extends Thread { static int counter = 0; static Object lock = new Object(); private Map<String, Integer> map; private String key = this.getId() + ""; MapPutThread(Map<String, Integer> map) { synchronized (lock) { counter++; } this.map = map; } public void run() { for (int i = 1; i <= T.NUMBER; i++) { map.put(key, i); } synchronized (lock) { counter--; } }}class MapGetThread extends Thread { static int counter = 0; static Object lock = new Object(); private Map<String, Integer> map; private String key = this.getId() + ""; MapGetThread(Map<String, Integer> map) { synchronized (lock) { counter++; } this.map = map; } public void run() { for (int i = 1; i <= T.NUMBER; i++) { map.get(key); } synchronized (lock) { counter--; } }}
运行结果:
Put time HashMapSync=3966ms.
Put time ConcurrentHashMap=1892ms.
Put time Hashtable=3892ms.
Get time HashMapSync=3812ms.
Get time ConcurrentHashMap=1828ms.
Get time Hashtable=3985ms.
- ConcurrentHashMap使用方法
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- ConcurrentHashMap
- concurrenthashmap
- ConcurrentHashMap
- 从程序员到项目经理(8):程序员加油站 -- 不要死于直率
- android Thumbnails
- TCHAR转为char
- 2013,我该这么做
- Mysql创建、删除用户
- ConcurrentHashMap使用方法
- No10、翻转句子中单词的顺序
- 16天记住7000考研单词
- 最好的编程博客调查(国外篇)
- 如何成为强大的程序员?
- java.util.Date的set方法问题(struts2类型转换)
- ARM 异常处理中宏代码分析
- 关系型数据库到HBase的数据储存方式变迁
- udp写服务器程序