HashMap原理介绍
Java 位运算说明
ConcurrentHashMap 分析
-
ConcurrentHashMap 在jdk 1.7 和jdk 1.8 中 的实现有很大不同.(参考1和参考2)
jdk 1.8 的改进:- 取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。
- 将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。
-
ConcurrentHashMap 如何做到线程安全
很多文章说 ConcurrentHashMap 是用的分段锁技术 来 实现高并发, 这是jdk1.7 中的实现, jdk1.8 中 使用的是 synchronized 每个 table 的node 对象来实现减少了锁的粒度。 在jdk 1.8 中 synchronized 进行了优化,效率和ReentrantLock 差不多。 -
ConcurrentHashMap 弱一致性的问题:
ConcurrentHashMap 有很多操作,get ,put, iterator…, 这里来分析下这些方法的一致性- ConcurrentHashMap #get 早期 ConcurrentHashMap 存在弱一致性问题,主要是因为 put 操作 中 table 加了 volatile,但table中的元素不是volatile的,同时 get 操作没有 加锁。会出现写入的数据不是立刻可见,jdk 1.8 中 通过
pred.next = new Node<K,V>(hash, key,value, null);
来添加元素,put 的元素能立刻可见,不再是弱一致性
详细分析1 和详细分析2
2. ConcurrentHashMap #iterator 在并发时,线程增加的字段不是立刻可见的。还是弱一致性, 这里不能打印出6666 详细分析1
public static void main(String[] str){
//ConcurrentHashMap<String, String> testCHM = new ConcurrentHashMap<String, String>(); //一个线程在读concurrentHashMap, 另外一个线程在写concurrentHashMap,
//遍历的过程中写入没法读到当前的值.
HashMap<String, String> testCHM = new HashMap<String, String>(); //HashMap 中一个线程读,一个线程写会抛出异常ConcurrentModificationException
testCHM.put("1","11111");
testCHM.put("2","22222");
testCHM.put("3","33333");
testCHM.put("4","44444");
testCHM.put("5","55555");
testCHM.put("7","77777");
testCHM.put("8","88888");
new Thread(new Runnable() {
@Override
public void run(){
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------开始插入66666-------");
testCHM.put("6","66666");
System.out.println("----------结束插入666-----");
}
}).start();
for(Map.Entry entry: testCHM.entrySet()){
System.out.println(entry.getValue());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- ConcurrentHashMap 能否完全取代HashTable?:
ConcurrentHashMap 的iterator是弱一致性,而HashTable 是强一致性。并不能完全替换。