demo:
public static void main(String[] args) {
ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>(); // 初始化
for (int i=0; i<=1000; i++) {
map.put(i,i); // 添加元素
}
System.out.println(map.get(11)); // 获取元素
}
面试常问:
1.说一下ConcurrentHashMap的内部数据结构
ConcurrentHashMap内部由 数组加链表或红黑树组成。使用CAS和Sychronized锁来保证线程安全,相比HashTable来说,锁的颗粒度更小,读完全不加锁,写的时候写头node使用cas不加锁,写链表或者红黑树时只加锁头节点,因此可以并发写不同node上的节点。
2.跟HashMap相比有什么区别
并发安全,支持多线程扩容。
sizeCtl:
sizeCtl是控制标识符,不同的值表示不同的意义。
-1 正在初始化
-N 有N-1个线程执行扩容
正数或0 初始化大小/扩容阈值,为当前容量的0.75,若实际容量大于扩容阈值,则执行扩容
put():
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
//key或value为null时抛异常
if (key == null || value == null) throw new NullPointerException();
//获取key的hash
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();
· //如果此位置为null,则CAS添加元素
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);
· //如果此位置有元素,则synchronized的方式加锁
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
//key相同时,覆盖
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
//key不相同时,追加到尾部
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//若为红黑树,则追加
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
//链表长度大于8,尝试转为红黑树
if (binCount >= TREEIFY_THRESHOLD)
//尝试调整为红黑树(若node数组长度小于64依然走扩容)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
//初始化
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
//若sizeCtl<0,说明别的线程正在执行cas,当前线程让出时间片
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//第一次put,有且只有一个线程将sizeCtl设置为-1,其它线程让出时间片
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
//指定位置的节点
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
//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);
}
//尝试调整为红黑树(若node数组长度小于64依然走扩容)
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
//node数组长度小于64走扩容
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);
//node数组长度大于等于64调整为红黑树
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}