java hashtable源码_Hashtable HashMap ConcurrentHashMap 源码分析

1.Hashtable与HashMap区别比较

先来说说这两者之间的不同:

1.Hashtable 是JDK1.2出现的, 父类继承Dictionary 实现的是Map, HashMap父类是AbstractMap实现的Map

public class Hashtable extends Dictionary implements Map

public class HashMap   extends AbstractMap implements Map

2. Hashtable中的方法都是同步的, HashMap中的方法都是非同步的,

所以从性能上来说HashMap的效率比Hashtable快, 那么如果在多线程并发的环境下,HashMap如何实现同步处理

可以通过 :

Collections.synchronizedMap();

来实现线程同步, 看下synchronizedMap内部实现:

public static  Map synchronizedMap(Map m) {

return new SynchronizedMap<>(m);

}

private static class SynchronizedMap

implements Map, Serializable {

private static final long serialVersionUID = 1978198479659022715L;

private final Map m;     // Backing Map

final Object      mutex;        // Object on which to synchronize

SynchronizedMap(Map m) {

if (m==null)

throw new NullPointerException();

this.m = m;

mutex = this;

}

}

SynchronizedMap(Map m, Object mutex) {

this.m = m;

this.mutex = mutex;

}

3.Hashtable中key和value都不能为null, 在HashMap中, key可以为null,但这样的健值只有一个, Hashtable源代码:

public Object put(Object key, Object value) {

// Make sure the value is not null

if (value == null) throw new NullPointerException();

}

4. 两者遍历的方式的内部实现不同, HashMap使用了Iterator, Hashtable使用了Enumeration的方式

5. 哈希值的使用不同, Hashtable直接使用对象的hashcode, HashMap重新计算hash值

6. HashTable中hash数组默认大小为11, 增加的方式是old*2+1,  HashMap数组的默认大小是16, 而且一定是2的指数

那么接下来我们来分析一下HashMap的内部结构:

HashMap是一个数组和链表的组合体, 内部如下图:

70ee7c1673f9f6da380634618c35b4a7.png

源代码分析:

public V put(K key, V value) {

if (table == EMPTY_TABLE) {

inflateTable(threshold);

}

if (key == null)

return putForNullKey(value);

int hash = hash(key);

int i = indexFor(hash, table.length);

for (Entry e = table[i]; e != null; e = e.next) {

Object k;

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(hash, key, value, i);

return null;

}

当往HashMap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾

2.HashMap与ConcurrentHashMap源码分析

前面我们有说到,HashMap不是线程安全的, 那么在多线程环境下, 我们应该如何保证线程安全呢, 在JDK1.5之后我们引入ConcurrentHashMap类:

public class ConcurrentHashMap extends AbstractMap

implements ConcurrentMap, Serializable {

}

那么ConcurrentHashMap是具体如何实现线程安全的,从源代码中可以看出它引入了一个叫“分段锁”的概念,具体可以理解成将Map划分成N个小的Hashtable, 根据key.hashCode()来决定把key放到哪个HashTable中

在ConcurrentHashMap中,就是把Map分成N个Segment,put和get的时候, 根据key.hashCode() 算出放到哪个Segment中

public V put(K key, V value) {

Segment s;

if (value == null)

throw new NullPointerException();

int hash = hash(key);

int j = (hash >>> segmentShift) & segmentMask;

if ((s = (Segment)UNSAFE.getObject          // nonvolatile; recheck

(segments, (j <

s = ensureSegment(j);

return s.put(key, hash, value, false);

}

public V get(Object key) {

Segment s; // manually integrate access methods to reduce overhead

HashEntry[] tab;

int h = hash(key);

long u = (((h >>> segmentShift) & segmentMask) <

if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null &&

(tab = s.table) != null) {

for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile

(tab, ((long)(((tab.length - 1) & h)) <

e != null; e = e.next) {

K k;

if ((k = e.key) == key || (e.hash == h && key.equals(k)))

return e.value;

}

}

return null;

}

以上就是ConcurrentHashMap的工作机制, 通过把整个Map划分为N个Segment,可以提供相同的线程安全,效率提升N倍,默认提升16倍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值