像Concurrent dateHashMap等高性能的类具体的业务使用场景是哪些

博客分类:
好像今天没有什么源码读,那么就来看看的这两种HashMap有啥不一样的地方吧,在这之前先普及一下HashMap的一些基本知识:
  (1)放入HashMap的元素是key-value对。
  (2)底层说白了就是以前数据结构课程讲过的散列结构。
  (3)要将元素放入到hashmap中,那么key的类型必须要实现实现hashcode方法,默认这个方法是根据对象的地址来计算的,具体我也记不太清楚了,接着还必须覆盖对象的equal方法。
  用一张图来表示一下散列结构吧:
  在这里hashCode函数就是用于确定当前key应该放在hash桶里面的位置,这里hash桶可以看成是一个数组,最简单的通过一些取余的方法就能用来确认key应该摆放的位置,而equal函数则是为了与后面的元素之间判断重复。
  好了,这里我们接下来来看看java的这两种类库的用法吧:
  由于他们都实现了Map接口,将元素放进去的方法就是put(a,b),这里我们先来分析比较简单的HashMap吧:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
//获取当前key的hash值
int i = indexFor(hash, table.length);
//返回在hash桶里面的位置
for (Entry&K,V& e = table[i]; e != e = e.next) {
//遍历当前hansh桶后面的元素
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
//如果有相同的key,那么需要替换value
V oldValue = e.
e.recordAccess(this);
return oldV
//返回以前的value
modCount++;
addEntry(hash, key, value, i);
//放入entry
  这个函数其实本身还是很简单的,首先通过hash函数获取当前key的hash值,不过这里需要注意的是,对hashCode方法返回的值HashMap本身还会进行一些处理,具体什么样子的就不细说了,然后再调用indexFor方法用于确定当前key应该属于当前Hash桶的位置,接着就是遍历当前桶后面的链表了,这里equal方法就派上用场了,这里看到如果equal是相等的话,那么就直接用新的value来替换原来的value就好了。。。
  当然最多的情况还是,桶后面的链表没有与当前的key相同的,那么这个时候就需要调用addEntry方法,将要加入的key-value放入到当前的结构中了,那么接下来来看看这个方法的定义吧:
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size &= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
//相当于重新设置hash桶
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
createEntry(hash, key, value, bucketIndex);
//创建新的entry,并将它加入到当前的桶后面的链表中
  其实这个方法很简单,首先来判断当前的桶的大小,如果觉得太小的话,那么需要扩充当前桶的大小,这样可以让添加元素存放的更离散化一些,优化擦入和寻找的效率。
  然后就是创建一个新的entry,用于保存要擦入的key和value,然后再将其链到应该放的桶的链表上就好了。。
  好了,到这里位置,整个HashMap的擦入元素的过程就已经看的很清楚了,在整个这个过程中没有看到有加锁的过程,因此可以说明HashMap是不支持并发的,不是线程安全的,在并发的环境下使用会产生一些不一致的问题。。。
  因此java新的concurrent类库中就有了ConcurrentHashMap用于在并发环境中使用。。
  那么我们再来看看ConcurrentHashMap的put操作是怎么搞的吧:
public V put(K key, V value) {
Segment&K,V&
if (value == null)
throw new NullPointerException();
int hash = hash(key);
//获取hash值
int j = (hash &&& segmentShift) & segmentM
if ((s = (Segment&K,V&)UNSAFE.getObject
// recheck
(segments, (j && SSHIFT) + SBASE)) == null) //
in ensureSegment
//用于获取相应的片段
s = ensureSegment(j);
//这里表示没有这个片段,那么需要创建这个片段
return s.put(key, hash, value, false);
//这里就有分段加锁的策略
  这里刚开始跟HashMap都差不太多吧,无非是先获取当前key的hash值,但是接下来进行的工作就不太一样了,这里就有了一个分段的概念:
  ConcurrentHashMap将整个Hash桶进行了分段,也就是将这个大的数组分成了几个小的片段,而且每个小的片段上面都有锁存在,那么在擦入元素的时候就需要先找到应该插入到哪一个片段,然后再在这个片段上面进行擦入,而且这里还需要获取锁。。。。
  那我们来看看这个segment的put方法吧:
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
//这里的锁是计数锁,同一个锁可以被同一个线程获取多次,但是不能被不同的线程获取
HashEntry&K,V& node = tryLock() ? null :
//如果获取了当前的segment的锁,那么node为null,待会自己分配就好了
scanAndLockForPut(key, hash, value);
//如果没有加上锁,那么等吧,有可能的话还要分配entry,反正有时间干嘛不多做一些事情
//这里表示已经获取了锁,那么将在相应的位置放入entry
HashEntry&K,V&[] tab =
int index = (tab.length - 1) &
HashEntry&K,V& first = entryAt(tab, index);
//找到存放entry的桶,然后获取第一个entry
for (HashEntry&K,V& e =;) {
//从当前的第一个元素开始
if (e != null) {
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
//如果key相等,那么直接替换元素
oldValue = e.
if (!onlyIfAbsent) {
if (node != null)
node.setNext(first);
node = new HashEntry&K,V&(hash, key, value, first);
int c = count + 1;
if (c & threshold && tab.length & MAXIMUM_CAPACITY)
//如果元素太多了,那么需要重新调整当前的hash结构,让桶变多一些,这样元素放的更离散一些
rehash(node);
setEntryAt(tab, index, node);
oldValue =
} finally {
//这里必须要在finally里面释放已经获取的锁,这样才能保证锁一定会被释放
return oldV
 其实在这里ConcurrentHashMap和HashMap的区别就已经很明显了:
  (1)ConcurrentHashMap对整个桶数组进行了分段,而HashMap则没有
  (2)ConcurrentHashMap在每一个分段上都用锁进行保护,从而让锁的粒度更精细一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。。。
  最后用一张图来表来说明一下ConcurrentHashMap吧:
  最后,在并发的情况下,要么使用concurrent类库中提供的容器,要么就需要自己来管理数据的同步问题了。。。
HashMap与ConcurrentHashMap的测试报告
测试平台:
CPU:Intel Pentium(R) 4 CPU 3.06G
操作系统:window server 2003
一、HashMap与ConcurrentHashMap简单put操作的时间对比
1、HashMap测试
A、程序代码:
package test0908;
import java.util.Map;
import java.util.HashM
public class HashmapTest {
public static void main(String []args){
Map&Integer,Integer& hashmap = new HashMap&Integer,Integer&();
int tt=13;
而循环100万
Hashmap.put(i,”aaa”),
用时time = 2563ms
long begin1 = System.currentTimeMillis();
for(int i=0; i&1000000; i++){
tt = Math.abs(tt*(tt-i)-119);
hashmap.put(tt, tt);
//System.out.println(hashmap.get(tt));
System.out.println("time="+(System.currentTimeMillis() - begin1)+"ms.");
B、测试结果截图(循环100万次):
当put操作循环10万次时,得到time = 344ms,
循环50万次时,得到time = 1657ms,
循环100万次时,得到time =4094ms。
2、ConcurrentHashMap测试
A、程序代码:
package test0908;
import java.util.concurrent.ConcurrentHashM
public class conHashmapTest{
public static void main(String []args){
ConcurrentHashMap&Integer,Integer& chashmap = newConcurrentHashMap&Integer,Integer&();
int tt=13;
long begin1 = System.currentTimeMillis();
for(int i=0; i&1000000; i++){
tt = Math.abs(tt*(tt-i)-119);
chashmap.put(tt, tt);
//System.out.println(hashmap.get(tt));
System.out.println("time="+(System.currentTimeMillis() - begin1)+"ms.");
B、测试结果截图(循环100万次):
当put操作循环10万次时,得到time =281ms,
循环50万次时,得到time = 1376ms,
循环100万次时,得到time =3625ms,
二、HashMap与ConcurrentHashMap
put操作的最多个数对比(即内存溢出)
1、HashMap测试
测试结果截图:
运行程序,内存初值为:846M,内存峰值为:931M,put计数=1,030,604
2、ConcurrentHashMap
测试结果截图:
运行程序,内存初值为:847M,内存峰值为:931M,put计数=1,030,238
三、HashMap与ConcurrentHashMap
多线程操作的测试
1、HashMap测试结果截图:(10个put线程,8个get线程)
平均每秒的get次数/总get次数
平均每秒的put次数/总Put次数
2、ConcurrentHashMap
测试结果截图 :(10个put线程,8个get线程)
3、以上均设置睡眠1ms时, 平均每个线程达到510多;
每秒平均put的次数随线程的个数增加而增加,
4、注:当put线程数量为100,get线程数量为90时,HashMap就开始出现性能下降的情形,CPU使用率达到45%左右,且put、get的个数要明显少于ConcurrentHashMap的个数;
而使用ConcurrentHashMap时,则线程很稳定,CPU使用率不超过12%时。
测试结果截图:
与concurrenthashmap相比,Put,get线程达到100个条件下,hashmap要少5500左右
A、HashMap测试图:
B、ConcurrentHashMap测试图:
5、经反复测试发现,只要创建的put和get的线程总数达到180个以上时,HashMap的性能就开始下降。而当创建的put和get的线程总数达到256个以上时,ConcurrentHsahMap的性能也开始下降。
性能下降:CPU的使用率开始增加,平均每秒put和get的个数开始下降,即出现若线程再增加,而put和get反而减少。
发一篇原贴文章真不容易啊,尤其是还这么多图片的。嘻嘻!搞了我半个多钟头。这个测试报告是写给项目经理看的,但是很多同志说看不懂这个报告里面有什么../? 所以发上来希望各位高手狠狠的给点意见,小弟在此谢了!
最后贴上第三个测试中concurrenthashmap的源程序:
package test0908;
import java.util.concurrent.ConcurrentHashM
public class CHashmapTest {
public static void main(String []args){
count c = new count();
ConcurrentHashMap&Integer,Integer& chm = new ConcurrentHashMap&Integer,Integer&();
for(int i=0; i&50; i++){
new putCHashmapThread(chm,c).start();//put操作
for(int i =0 ; i&45; i++){
new getCHashmapThread(chm,c).start();
ProbeThread pt = new ProbeThread(c);
//监听线程
pt.start();
}catch(Exception e){
System.out.println(e.getMessage());
class putCHashmapThread extends Thread{
//put操作线程
private ConcurrentHashMap&Integer,Integer& chm =
private count c =
public putCHashmapThread(ConcurrentHashMap&Integer,Integer& chm,count c){
this.chm =
public void run(){
int tt = 13;
int i = 1;
while(true){
tt = Math.abs(tt*(tt-i)-119);
chm.put(tt, tt);
c.addcount1();
//put操作计数
Thread.sleep(1);
//System.out.println(i);
}catch(Exception e){
System.out.println(e.getMessage());
class getCHashmapThread extends Thread{
//get操作线程
private ConcurrentHashMap&Integer,Integer& chm =
private count c =
public getCHashmapThread(ConcurrentHashMap&Integer,Integer& chm,count c){
this.chm =
public void run(){
int tt = 13;
int i = 1;
while(true){
tt = Math.abs(tt*(tt-i)-119);
chm.get(tt);
c.addcount2();
//get操作计数
Thread.sleep(1);
//System.out.println(i);
}catch(Exception e){
System.out.println(e.getMessage());
class count{
private static int count1 = 1, count2 = 1;
public int getcount1() {
return count1;
public int getcount2(){
return count2;
public void addcount1(){
public void addcount2(){
class ProbeThread extends Thread {
//监听线程
private boolean run =
public ProbeThread(count cc) {
@SuppressWarnings("static-access")
public void run() {
int c1=0, c2=0;
int cc1 = 0, cc2 = 0;
while(this.run) {
c2 = cc.getcount1();
cc2 =cc.getcount2();
System.out.println("put:"+"["+(c2-c1)/2+"/"+c2+"]"+"
get:"+"["+(cc2-cc1)/2+"/"+cc2+"]");
cc1 = cc2;
Thread.sleep();
} catch(Exception ex) {
System.out.println("Error[ProbeThread.run]:"+ex.getMessage());
浏览: 664218 次
来自: 吉林
Selenium自动化测试从入门到精通(Java版)百度网盘地 ...
我喜欢代码简洁易读,服务稳定的推送服务,前段时间研究了一下go ...
楼主,请教一点问题.现在我们需要在excel 的页脚里面加上图 ...
使用Timer会有各种各样的问题好嘛?!书上推荐使用Sched ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'推荐这篇日记的豆列
&&&&&&&&&&&&鍗氬?鍒嗙被锛

我要回帖

更多关于 against the current 的文章

 

随机推荐