关于采用HashMap作为本地缓存遇到的问题
2013-07-20 12:37
381 查看
最近在做一个的项目,我所要完成的部分是对数据的清洗操作,当收到一条记录时,对记录做相应的适配,然后将适配后的数据返回。做适配就是要与之前的数据进行对比,所以需要对之前的数据做一个缓存,初步考虑用HashMap来进行缓存数据。因为我们的数据量是比较大的,一天大概是2亿条记录,一条记录是36个字段,字段之间用特殊的分隔符隔开。程序中使用了多线程,但是由于我对HashMap操作时,没有使用同步,导致CPU的使用率一直标的很高,并提示温度过高,我很郁闷,不得不将程序停止。事后,同事问我,是否程序中有死循环,其实代码逻辑很简单,连循环都没有,后来通过检查知道HashMap是非线程安全的,在多线程程序一定要注意。下面是对HashMap一些分析:(摘自http://blog.sina.com.cn/s/blog_4a1f59bf0100o98k.html)
从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个Entry[]:
一般我们声明HashMap时,使用的都是默认的构造方法:HashMap<K,V>,看了代码你会发现,它还有其它的构造方法:HashMap(intinitialCapacity,floatloadFactor),其中参数initialCapacity为初始容量,loadFactor为加载因子,而之前我们看到的threshold
=(int)(capacity*loadFactor);如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。
JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的线程安全类。
JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性。同时进行的读和写操作都可以并发地执行。
所以在开始的测试中,如果我们采用ConcurrentHashMap,它的表现就很稳定,所以以后如果使用Map实现本地缓存,为了提高并发时的稳定性,还是建议使用ConcurrentHashMap。
publicVput(Kkey,Vvalue){ if(key==null) returnputForNullKey(value); inthash=hash(key.hashCode()); inti=indexFor(hash,table.length); for(Entry<K,V>e=table[i];e!=null;e=e.next){ Objectk; if(e.hash==hash&&((k=e.key)==key||key.equals(k))){ VoldValue=e.value; e.value=value; e.recordAccess(this); returnoldValue; } } modCount++; addEntry(hash,key,value,i); returnnull; }
voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){
Entry<K,V>e=table[bucketIndex];
table[bucketIndex]=newEntry<K,V>(hash,key,value,e);
if(size++>=threshold)
resize(2*table.length);
}
从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个Entry[]:
voidresize(intnewCapacity){
Entry[]oldTable=table;
intoldCapacity=oldTable.length;
if(oldCapacity==MAXIMUM_CAPACITY){
threshold=Integer.MAX_VALUE;
return;
}
Entry[]newTable=newEntry[newCapacity];
transfer(newTable);
table=newTable;
threshold=(int)(newCapacity*loadFactor);
}
一般我们声明HashMap时,使用的都是默认的构造方法:HashMap<K,V>,看了代码你会发现,它还有其它的构造方法:HashMap(intinitialCapacity,floatloadFactor),其中参数initialCapacity为初始容量,loadFactor为加载因子,而之前我们看到的threshold
=(int)(capacity*loadFactor);如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。
JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的线程安全类。
JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性。同时进行的读和写操作都可以并发地执行。
所以在开始的测试中,如果我们采用ConcurrentHashMap,它的表现就很稳定,所以以后如果使用Map实现本地缓存,为了提高并发时的稳定性,还是建议使用ConcurrentHashMap。
相关文章推荐
- 有关于RTX本地缓存的问题
- 有关于RTX本地缓存的问题
- 关于模型数组进行本地保存中遇到的一些问题
- 关于本地缓存登陆 和 域用户将计算机加入域的问题及登录过程- -
- 关于本地通遇到的问题及解决的方法
- 关于本地缓存登陆 和 域用户将计算机加入域的问题及登录过程- -
- 本地缓存的实现以及遇到的问题
- 关于缓存常遇到的问题
- 一个关于自定义类型作为HashMap的key的问题
- 关于数组和指针作为参数时遇到的问题
- 本地缓存的实现以及遇到的问题
- 关于本地缓存登陆 和 域用户将计算机加入域的问题及登录过程- -
- 关于ImageLoader加载本地缓存下来的图片的问题?
- 1, 本地缓存的实现以及遇到的问题
- 关于这周工作中遇到的关于缓存问题的记录
- 关于老鹰主机域名注册续费主机,我购买时遇到的问题
- dubbo本地缓存提供者信息配置问题
- 关于打包上传遇到的比较蛋疼的小问题
- 关于把.net 2.0的项目升级到.net4.0遇到的一些问题
- 关于TSP项目中遇到的一些问题,及解决方法