zoukankan      html  css  js  c++  java
  • 集合框架—常用的map集合

    1、Collections.synchronizedMap()

    实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException

    ConcurrentHashMap从类的命名就能看出,它必然是个HashMapCollections.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多 行,实现上也和原来的分段式存储有很大的区别。

    主要设计上的变化有以下几点:

    1. 不采用segment而采用node,锁住node来实现减小锁粒度。
    2. 设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
    3. 使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
    4. 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

    1TreeMap 类不仅实现了 Map 接口,还实现了 Map 接口的子接口 java.util.SortedMap 

    2TreeMap 类中不允许键对象为 null 或是基本数据类型,这是因为 TreeMap 中的对象必须是可排序的(即对象需要实现 java.lang.Comparable 接口) 

    3:在创建 TreeMap 对象时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序;如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。 

    4:在添加、删除和定位映射关系上,TreeMap类要比HashMap类的性能差一些,

    5:在需要排序时,利用现有的 HashMap,创建一个 TreeMap 类型的实例

    Java代码  

    1. import java.util.Collections;  
    2. import java.util.HashMap;  
    3. import java.util.Iterator;  
    4. import java.util.Map;  
    5. import java.util.TreeMap;  
    6.   
    7. public class TestCollection {  
    8.   
    9.     public static void main(String[] args) {  
    10.         System.out.println("开始:");    
    11.                 
    12.         Person person1 = new Person("马先生"220181);  
    13.         Person person2 = new Person("李先生"220193);  
    14.         Person person3 = new Person("王小姐"220186);  
    15.           
    16.         Map<Number, Person> map = new HashMap<Number, Person>();  
    17.         map.put(person1.getId_card(), person1);  
    18.         map.put(person2.getId_card(), person2);  
    19.         map.put(person3.getId_card(), person3);  
    20.           
    21.         // HashMap  
    22.         System.out.println("HashMap,无序:");  
    23.         for (Iterator<Number> it = map.keySet().iterator(); it.hasNext();) {  
    24.             Person person = map.get(it.next());  
    25.             System.out.println(person.getId_card() + " " + person.getName());  
    26.         }  
    27.           
    28.         // TreeMap  
    29.         System.out.println("TreeMap,升序:");  
    30. //创建 TreeMap 时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序
    31.         TreeMap<Number, Person> treeMap = new TreeMap<Number, Person>();  
    32.         //将指定映射中的所有映射关系复制到此映射中。
    33. treeMap.putAll(map);           for (Iterator<Number> it = treeMap.keySet().iterator(); it.hasNext();) {  
    34.             Person person = treeMap.get(it.next());  
    35.             System.out.println(person.getId_card() + " " + person.getName());  
    36.         }  
    37.           
    38.         System.out.println("TreeMap,降序:");
    39. //Collections.reverseOrder()返回一个比较器,它强行逆转指定比较器顺序 
    40. //如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。  
    41.         TreeMap<Number, Person> treeMap2 =   
    42.             new TreeMap<Number, Person>(Collections.reverseOrder());  
    43.         treeMap2.putAll(map);  
    44.         for (Iterator it = treeMap2.keySet().iterator(); it.hasNext();) {  
    45.             Person person = (Person) treeMap2.get(it.next());  
    46.             System.out.println(person.getId_card() + " " + person.getName());  
    47.         }  
    48.           
    49.         System.out.println("结束!");  
    50.     }  
    51. }  
  • 相关阅读:
    字节数组倒序
    Windows和Linux下apache-artemis-2.10.0安装配置
    delphi superobject解析复杂json
    delphi实现起泡提示效果
    启动delphi 2010 后无响应,过很久(几十秒后),出现错误框“displayNotification:堆栈溢出
    SVN更新失败,提示locked 怎么破
    jQuery学习之旅 Item10 ajax快餐
    jQuery学习之旅 Item9 动画效果
    jQuery学习之旅 Item8 DOM事件操作
    jQuery学习之旅 Item7 区别this和$(this)
  • 原文地址:https://www.cnblogs.com/domi22/p/8047089.html
Copyright © 2011-2022 走看看