聊聊spring中常用的集合

最近看spring源码分析,发现一些迷惑的地方,spring容器对于像ConcurrentHashMap的bean定义信息,HashSet,Collections.synchronizedSet、Collections.synchronizedMap等不同集合处理,现分析一下其中HashMap,ConcurrentHashMap,Collections.synchronizedMap的不同以及应用场景。

  1. HashMap
    1.1. 结构
    是由数组和链表共同组成的结构,采用链地址法解决哈希冲突,只允许一个key为null,多个value为null,所以不能通过get()方法判断是否存在某key值;map值保存在transient Entry[] table;Entry是一个类似树结构的包含key,value,next,hash值。通过链表结构将具有相同的hash值的key设置在链表的头。

    1.2. 非线程安全
    线程安全问题都是由全局变量及静态变量引起的 ,是指多个线程运行一段代码与单个线程多次运行结果一样而在hashMap中,多个线程进行put操作的时候,可能会存在resize扩容的多种情况,从而出现转移的过程中丢失数据,或者扩容失败等情况,因此其在多线程环境下不是线程安全的,需手动实现同步机制。

  2. HashTable
    2.1. 结构
    和hashMap类似数据结构,但继承Dictionary,但是其key还是value都不能为null。

    2.2. 性能
    除了containValue,rehash方法外,其他的操作方法都增加了synchronized锁机制进行整个hash表的锁定,多线程环境下是线程安全。

  3. ConcurrentHashMap
    3.1. 结构
    采用线程锁分段技术final修饰的 Segment[]默认长度为16,类似16个桶,其成员变量也是final修饰的。其初始化时候采用了除第一个外的延迟实例化,其类似HashMap结构,其内部的HashEntry包含final 修饰的key,volatile修饰的value,volatile修饰的next,final 修饰的hash,但value,hash是volatile修饰,保证了可见性,同时Segment又继承了ReentrantLock,并不允许key或者value为null,否则HashEntry的key/value没有映射完成就被其他线程所见

    3.2. 性能
    3.2.1. 弱一致性
    get与containsKey方法没有使用锁机制,通过Unsafe对象的getObjectVolatile()方法提供的原子读语义,获取Segment以及对应的链表,从而遍历链表判断值得存在等信息,但是遍历过程中其他线程可能对链表结构做了调整,因此get和containsKey返回的可能是过时的数据,这一点是ConcurrentHashMap在弱一致性上的体现。
    3.2.2. 线程安全性
    使用锁分段技术一次只锁住一个segment对象,但是size(),containsValue方法例外,按顺序锁定segment组, ConcurrentHashMap实际上是Hashtable的升级版,具备线程安全外还增加了迭代器快速失败行为的异常处理,也就是说,通过ConcurrentHashMap对Iterator迭代器结构的修改不会抛出异常,因为iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,而Hashtable会抛出异常,因而就Hashtable来说,如果迭代器修改了映射结构,那么遍历的结果是不确定的,而ConcurrentHashmap支持只允许一个线程对迭代器的映射结构进行修改。
    3.2.3. 值处理
    remove方法中每次删除一个节点时,会将删除节点之前的所有节点 拷贝一份组成一个新的链,而将当前节点的上一个节点的next指向当前节点的下一个节点,是一种更加细粒度的happens-before,操作的是原链表。
    size方法先给3次机会,不lock所有的Segment,遍历所有Segment,累加各个Segment的大小得到整个Map的大小,如果某相邻的两次计算相等,则返回,否则加锁遍历所有的Segment才能算出整个Map的大小,

  4. Collections.synchronizedMap()
    4.1. 结构
    通过定义一个内部类SynchronizedMap进行数据处理,该map通过定义一个final Object mutex,通过synchronized关键字进行了每个方法的mutex对象同步控制。

    4.2. 性能 –强一致性
    虽然该map进行了所有方法的对象锁定,但是也会出现一些不安全现象。比如 if(map.containsKey(‘key’)){map.remove(key); } 由于代码块没有进行同步控制,当多个线程访问的时候会出现会出现错误处理;另一个情况,通过map得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”,所以当多个线程进行遍历和修改map的时候,就会抛出ConcurrentModificationException,为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过集合类的 toArray()方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。

对于上述map中使用到的transient,volatile概念以及TreeMap等结构下次学习运用之后再整理哈^_^

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值