集合框架—常用的map集合
1、Collections.synchronizedMap()
实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。
ConcurrentHashMap从类的命名就能看出,它必然是个HashMap。Collections.synchronizedMap()可以接收任意Map实例作为封装。
=====================================================================================
下面的例子显示java.util.Collections.synchronizedMap()方法的使用
package com.yiibai;
import java.util.*;
public class CollectionsDemo {
public static void main(String[] args) {
// create map
Map<String,String> map = new HashMap<String,String>();
// populate the map
map.put(“1”,“TP”);
map.put(“2”,“IS”);
map.put(“3”,“BEST”);
// create a synchronized map
Map<String,String> synmap = Collections.synchronizedMap(map);
System.out.println(“Synchronized map is :”+synmap);
}
}
现在编译和运行上面的代码示例,将产生以下结果。
Synchronized map is :{3=BEST, 2=IS, 1=TP}
也不会抛出ConcurrentModificationException。
===============================================================================
2、ConcurrentHashMap
1:线程安全,
2:Iterator的迭代方式采用的是fail-safe的错误机制
ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求,ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响.
3:而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。
4:同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。
ConcurrentHashMap采用了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的提高了高并发环境下的处理能力。但同时,由于不是对整个Map加锁,导致一些需要扫描整个Map的方法(如size(), containsValue())需要使用特殊的实现,另外一些方法(如clear())甚至放弃了对一致性的要求(ConcurrentHashMap是弱一致性的,
ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
源代码:
static final class Segment<K,V> extends ReentrantLock implements Serializable{
并发度
并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。ConcurrentHashMap默认的并发度为16,但用户也可以在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap会使用大于等于该值的最小2幂指数作为实际并发度(假如用户设置并发度为17,实际并发度则为32)。运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在构造过程中根据concurrency level被相应的计算出来。
如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPU cache命中率会下降,从而引起程序性能下降。(文档的说法是根据你并发的线程数量决定,太多会导性能降低)
JDK8中的实现
ConcurrentHashMap在JDK8中进行了巨大改动,很需要通过源码来再次学习下Doug Lea的实现方法。
它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由”数组”+链表+红黑树的方式思想(JDK7与JDK8中HashMap的实现),但是为了做到并发,又增加了很多辅助的类,例如TreeBin,Traverser等对象内部类。
重要的属性
首先来看几个重要的属性,与HashMap相同的就不再介绍了,这里重点解释一下sizeCtl这个属性。可以说它是ConcurrentHashMap中出镜率很高的一个属性,因为它是一个控制标识符,在不同的地方有不同用途,而且它的取值不同,也代表不同的含义。
- 负数代表正在进行初始化或扩容操作
- -1代表正在初始化
- -N 表示有N-1个线程正在进行扩容操作
- 正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小,这一点类似于扩容阈值的概念。还后面可以看到,它的值始终是当前ConcurrentHashMap容量的0.75倍,这与loadfactor是对应的。
总结
JDK6,7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。
jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,所以jdk8 中完全重写了concurrentHashmap,代码量从原来的1000多行变成了 6000多 行,实现上也和原来的分段式存储有很大的区别。
主要设计上的变化有以下几点:
- 不采用segment而采用node,锁住node来实现减小锁粒度。
- 设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
- 使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
- sizeCtl的不同值来代表不同含义,起到了控制的作用。
至于为什么JDK8中使用synchronized而不是ReentrantLock,我猜是因为JDK8中对synchronized有了足够的优化吧。
3、LinkedHashMap
是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
Map<Integer,String> map = new LinkedHashMap<Integer,String>();
map.put(6, “apple”);
map.put(3, “banana”);
map.put(2,”pear”);
for (Iterator it = map.keySet().iterator();it.hasNext();)
{
Object key = it.next();
System.out.println( key+”=”+ map.get(key));
}
运行结果如下:
*************************LinkedHashMap*************
6=apple
3=banana
2=pear
4、TreeMap
1:TreeMap 类不仅实现了 Map 接口,还实现了 Map 接口的子接口 java.util.SortedMap。
2:TreeMap 类中不允许键对象为 null 或是基本数据类型,这是因为 TreeMap 中的对象必须是可排序的(即对象需要实现 java.lang.Comparable 接口)
3:在创建 TreeMap 对象时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序;如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。
4:在添加、删除和定位映射关系上,TreeMap类要比HashMap类的性能差一些,
5:在需要排序时,利用现有的 HashMap,创建一个 TreeMap 类型的实例
Java代码
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.TreeMap;
- public class TestCollection {
- public static void main(String[] args) {
- System.out.println(“开始:“);
- Person person1 = new Person(“马先生“, 220181);
- Person person2 = new Person(“李先生“, 220193);
- Person person3 = new Person(“王小姐“, 220186);
- Map<Number, Person> map = new HashMap<Number, Person>();
- map.put(person1.getId_card(), person1);
- map.put(person2.getId_card(), person2);
- map.put(person3.getId_card(), person3);
- // HashMap
- System.out.println(“HashMap,无序:“);
- for (Iterator<Number> it = map.keySet().iterator(); it.hasNext();) {
- Person person = map.get(it.next());
- System.out.println(person.getId_card() + ” “ + person.getName());
- }
- // TreeMap
- System.out.println(“TreeMap,升序:“);
- //创建 TreeMap 时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序
- TreeMap<Number, Person> treeMap = new TreeMap<Number, Person>();
- //将指定映射中的所有映射关系复制到此映射中。
- treeMap.putAll(map); for (Iterator<Number> it = treeMap.keySet().iterator(); it.hasNext();) {
- Person person = treeMap.get(it.next());
- System.out.println(person.getId_card() + ” “ + person.getName());
- }
- System.out.println(“TreeMap,降序:“);
- //Collections.reverseOrder()返回一个比较器,它强行逆转指定比较器顺序
- //如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。
- TreeMap<Number, Person> treeMap2 =
- new TreeMap<Number, Person>(Collections.reverseOrder());
- treeMap2.putAll(map);
- for (Iterator it = treeMap2.keySet().iterator(); it.hasNext();) {
- Person person = (Person) treeMap2.get(it.next());
- System.out.println(person.getId_card() + ” “ + person.getName());
- }
- System.out.println(“结束!“);
- }
- }