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 <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
    return new SynchronizedMap<>(m);
}

private static class SynchronizedMap<K,V>
    implements Map<K,V>, Serializable {
    private static final long serialVersionUID = 1978198479659022715L;

    private final Map<K,V> m;     // Backing Map
    final Object      mutex;        // Object on which to synchronize

    SynchronizedMap(Map<K,V> m) {
        if (m==null)
            throw new NullPointerException();
        this.m = m;
        mutex = this;
    }
}    

    SynchronizedMap(Map<K,V> 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是一个数组和链表的组合体, 内部如下图:

Hashtable HashMap ConcurrentHashMap 源码分析_第1张图片


      源代码分析: 

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<K,V> 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<K, V> extends AbstractMap<K, V>
        implements ConcurrentMap<K, V>, 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<K,V> s;
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key);
    int j = (hash >>> segmentShift) & segmentMask;
    if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
         (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
        s = ensureSegment(j);
    return s.put(key, hash, value, false);
}

public V get(Object key) {
    Segment<K,V> s; // manually integrate access methods to reduce overhead
    HashEntry<K,V>[] tab;
    int h = hash(key);
    long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
    if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
        (tab = s.table) != null) {
        for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
                 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
             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倍

你可能感兴趣的:(java,HashMap)