ConcurrentHashMap的实现原理是分段锁?你Out了
Posted 沛沛老爹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentHashMap的实现原理是分段锁?你Out了相关的知识,希望对你有一定的参考价值。
前言
Java后端开发面试的时候,一场好的面试,是无论如何也绕不开并发编程的。并发编程里面往往有个很重要的类可能会被拿出来探讨:ConcurrentHashMap。
ConcurrentHashMap是HashMap的线程安全版。大家都知道HashMap的高性能,但是HashMap不是线程安全的。所以为了解决这个问题,就推出了Concurrent版本。
在和面试官探讨这个问题的时候,大家一般都会把分段锁Segment的实现原理拿出来大讲特讲(因为除了这个分段式锁外,好像别的也没有什么不同的)。
模拟面试场景
面试官:请简述下ConcurrentHashMap的实现原理。
面试者:
1、ConcurrentHashMap是由Segment数组和HashEntry数组组成。它内部细分成了若干个小的HashMap,每个小的HashMap被称为段(Segment),默认情况下,一个ConcurrentHashMap被细分为16个Segment。
2、Segment是一种可重入锁ReentrantLock,它在ConcurrentHashMap中扮演锁的角色;HashEntry用来存储键值对数据。
3、一个 ConcurrentHashMap 里包含一个 Segment 数组,Segment 的结构和 HashMap类似,是一种数组+链表结构。
4、每个Segment里包含一个HashEntry数组,当要对HashEntry数组的数据进行修改时,必须要先获得它对应的Segment锁。
真正的ConcurrentHashMap
此时,是不是感觉自己答的很溜?这个问题中的重要点都回答了。
但是,这真的是你用的ConcurrentHashMap吗?还是你Out了?
其实,你还真是Out了。上面的回答是没问题的,但是知识点Out了。这个ConcurrentHashMap分段锁的实现是JDK1.7的了(网上的很多知识都是这样的)。JDK1.8之后的实现有了很大的改变了
ConcurrentHashMap源码
说多了浪费口水,还是直接看源码吧。
ConcurrentHashMap代码在java.util.concurrent包中(不要搞错了)
Node部分源码
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable
/* ---------------- Constants -------------- */
//... 省略部分代码
//实现重点代码
/* ---------------- Nodes -------------- */
/**
* Key-value entry. This class is never exported out as a
* user-mutable Map.Entry (i.e., one supporting setValue; see
* MapEntry below), but can be used for read-only traversals used
* in bulk tasks. Subclasses of Node with a negative hash field
* are special, and contain null keys and values (but are never
* exported). Otherwise, keys and vals are never null.
*/
static class Node<K,V> implements Map.Entry<K,V>
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next)
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
public final K getKey() return key;
public final V getValue() return val;
public final int hashCode() return key.hashCode() ^ val.hashCode();
public final String toString() return key + "=" + val;
public final V setValue(V value)
throw new UnsupportedOperationException();
public final boolean equals(Object o)
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
/**
* Virtualized support for map.get(); overridden in subclasses.
*/
Node<K,V> find(int h, Object k)
Node<K,V> e = this;
if (k != null)
do
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
while ((e = e.next) != null);
return null;
//... 省略部分代码
我们会发现,ConcurrentHashMap类中中没有找到Segment的实现的影子。而是在类中发现了很多使synchronized的方式来实现的。
put源码
我们看下常用的put方法的实现。发现使用的是synchronized来实现的。
public V put(K key, V value)
return putVal(key, value, false);
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent)
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;)
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null)
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else
V oldVal = null;
synchronized (f)
if (casTabAt(tab, i, null, r))
//...省略代码
···
CAS部分代码
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v)
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
想要了解更多CAS部分的知识,可以参考这篇文章CAS(Compare and swap)比较并交换算法解析
总结
新版的ConcurrentHashMap和Segment分段锁没有任何关系。它的实现方式和HashMap有点大同小异:数组+链表+红黑树。
更新后的变化
类型 | JDK1.7 | JDK1.8 |
---|---|---|
数据结构 | Segment分段锁 | 数组+链表+红黑树 |
线程安全机制 | segment的分段锁机制 | CAS+Synchorized机制 |
锁的粒度 | 每段Segment加锁,粒度大 | 每个Node元素加锁,粒度更细 |
遍历时间复杂度 | O(n) | O(logN) |
好了,如果你把上面新的知识结合老的知识一起给到面试官,我想面试官肯定会想,这家伙是个可造之才,让HR通知他,明天赶紧来上班。 |
以上是关于ConcurrentHashMap的实现原理是分段锁?你Out了的主要内容,如果未能解决你的问题,请参考以下文章