JAVA之ConcurrentHashMap源码深度分析

欢迎转载,请附上出处:

我们首先来看一下ConcurrentHashMap类的声明:

<K, V> extends AbstractMap<K, V>implements ConcurrentMap<K, V>, Serializable

其中,这个类继承了java.util.AbstractMap中已有的实现,这个在前面整理HashMap的时候已经提过了,重点看后面实现的接口ConcurrentMap和Serializable。Serializable是做序列化处理的,而ConcurrentMap的定义又如下:

<K, V> extends Map<K, V> {V putIfAbsent(K key, V value);boolean remove(Object key, Object value);boolean replace(K key, V oldValue, V newValue);V replace(K key, V value);}

其中规定了4个方法。

这些方法都在ConcurrentHashMap实现了,后文会部分提到这些。

1. 构造方法和ConcurrentHashMap的Segment实现 先看下构造方法:

public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel)

有几个重载的方法,说这个参数最全的,有3个参数,除了HashMap中涉及到的loadFactor和initialCapacity外,还有一个concurrencyLevel,翻译过来就是并发级别或者并发度。

与此对应,ConcurrentHashMap中有一个segments数组对象,元素类型是ConcurrentHashMap的内部类Segment,而concurrencyLevel就是这个segments数组的大小。

我们来看下这个Segment类:

<

Segment扩展了ReentrantLock并实现了Serializable接口。除此之外,我们还发现这个类里实现的东西和java.util.HashMap非常相似。

实际上,这个类正是整个ConcurrentHashMap实现的关键。我想,作为这篇文章读者的您,应该会用到过各式各样的数据库,就拿Mysql的innoDB引擎来看,它除了支持表级锁意外,还支持行级锁,意义就在于这减小了锁粒度,当只对某行数据进行操作的时候,很可能没有必要限制同一个表中其它行的数据。在这个类中,这个Segment也是起到了同样的作用。每个Segment本身就是一个ReentrantLock,只有要修改的数据存在在同一个Segment,才有可能会需要锁定,这样就提高了多线程情况下效率,没必要所有线程全部等待锁。

2. get()方法源码分析 我们先看下get()方法的实现。

public V get(Object key) {Segment<K,V> s; // manually integrate access methods to reduce overheadHashEntry<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;}

实际上,就是通过key计算得到的hash值,确定对应的Segment对象,并用原子操作获取到对应的table和table中hash值对应的对象。

我们可以看到,在这个过程中,是没有显式用到锁的,仅仅是通过Unsafe类和原子操作,避免了阻塞,提高了性能。

3. put()和putIfAbsent()方法分析 先看下put()方法的源码:

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 ensureSegments = ensureSegment(j);return s.put(key, hash, value, false);}

我们看到其中最后是使用Segment的put()方法的调用,而putIfAbsent()的方法的调用,仅仅是最后一个参数不同。

我们进一步看下Segment的put()方法的调用:

final V put(K key, int hash, V value, boolean onlyIfAbsent) {HashEntry<K,V> node = tryLock() ? null :scanAndLockForPut(key, hash, value);V oldValue;try {HashEntry<K,V>[] tab = table;int index = (tab.length – 1) & hash;HashEntry<K,V> first = entryAt(tab, index);for (HashEntry<K,V> e = first;;) {if (e != null) {K k;if ((k = e.key) == key ||(e.hash == hash && key.equals(k))) {oldValue = e.value;if (!onlyIfAbsent) {e.value = value;++modCount;}break;}e = e.next;}else {if (node != null)node.setNext(first);elsenode = new HashEntry<K,V>(hash, key, value, first);int c = count + 1;if (c > threshold && tab.length < MAXIMUM_CAPACITY)rehash(node);elsesetEntryAt(tab, index, node);++modCount;count = c;oldValue = null;break;}}} finally {unlock();}return oldValue;}向上攀爬的。

JAVA之ConcurrentHashMap源码深度分析

相关文章:

你感兴趣的文章:

标签云: