hashmap线程不安全的表现及原因分析

电子常识

2640人已加入

描述

  Hashmap

  基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

  Hashmap重要参数

  HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。在Java编程语言中,加载因子默认值为0.75,默认哈希表元为101

  线程

  hashMap出现线程不安全的原因:

  HashMap的实现里没有锁的机制,因此它是线程不安全的。其实只要有锁的机制,可以通过锁实现线程安全,我们在读写HashMap对象的时候加锁,以保障这个对象的线程安全,但不代表HashMap本身是线程安全的,因为是外力(你自己加的锁)使然。为啥不在HashMap内部加锁让它变成线程安全?这样会增加单线程访问的资源消耗,即使没有多线程访问,也要每次检查、加锁、解锁。实际上有线程安全的Map,Collections里面有个静态方法可以返回一个线程安全版本的HashMappublic static 《K,V》 Map《K,V》 synchronizedMap(Map《K,V》 m) {

  return new SynchronizedMap《K,V》(m);

  }

  另外java5之后还提供了ConcurrentHashMap类,提供更高效的线程安全操作。

  hashMap出现线程不安全的表现:

  表现1:

  多个线程同时操作一个hashmap就可能出现不安全的情况:

  比如A B两个线程(A线程获数据 B线程存数据) 同时操作myHashMap

  1.B线程执行存放数据

  modelHashMap.put(“1”,“2”);

  2.A线程执行get获取数据

  modelHashMap.get(“1”)

  A线程获取的值本来应该是2,但是如果A线程在刚到达获取的动作还没执行的时候,

  线程执行的机会又跳到线程B,此时线程B又对modelHashMap赋值 如:modelHashMap.put(“1”,“3”);

  然后线程虚拟机又执行线程A,A取到的值为3,这样map中第一个存放的值 就会丢失。。。。。

  要保证值的准确,就要保证操作的原子性,就是保证A操作从头开始不能被打断。。所有要用同步关键字,或者使用java 1.5中的current新包中的ConcurrentHashMap,这是线程安全的,在java最新的并发包中,对之前非线程安全的工具,如hashMap List 都做了同步封转。

  表现2:

  一般我们声明HashMap时,使用的都是默认的构造方法:HashMap《K,V》,看了代码你会发现,它还有其它的构造方法:HashMap(int initialCapacity, float loadFactor),其中参数initialCapacity为初始容量,loadFactor为加载因子,而之前我们看到的threshold = (int)(capacity * loadFactor); 如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。

  表现3:

  构造entry《K,V》单链表时,也会出现不安全的情况。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分