首先做个分析:
hashMap,hashTable,ConcurrentHashMap,这三者之间的区别,HashMap是线程不安全的,在多线程的环境下,hashMap的put方法可能引起死循环,于是为了线程安全,出现了hashTable,hashTable解决多线程安全的问题是简单粗暴的加synchronized关键字,但是这种方法引起效率低下,于是ConcurrentHashMap出现了,下面主要介绍下ConcurrentHashMap。
ConcurrentHashMap实现的接口是ConcurrentMap
public class ConcurrentHashMap
extends AbstractMap
implements ConcurrentMap, Serializable {
成员变量,这里只列举出了ConcurrentHashMap特有的
static final int DEFAULT_CONCURRENCY_LEVEL = 16; //初始的并发等级,通过并发等级来确定Segment的大小
static final int MIN_SEGMENT_TABLE_CAPACITY = 2;// segment的最小值
static final int MAX_SEGMENTS = 1 << 16; //segment的最大值
ConcurrentHashMap的构造函数
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
// Find power-of-two sizes best matching arguments
int sshift = 0;//计算ssize左移的次数
int ssize = 1;//计算segment的大小
while (ssize < concurrencyLevel) {//segment的大小为2^n
++sshift;
ssize <<= 1;
}
this.segmentShift = 32 - sshift;
this.segmentMask = ssize - 1;//这个为什么要为Segment数组的长度 -1,主要是为了让低位为1,这样在做&运算确定Segment的索引时能够更加分散
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;//计算每个segment的大小
if (c * ssize < initialCapacity)//若是条件成立,表示c有余数,所以增加一个segment
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
// create segments and segments[0]//创建一个segment,并且放在 segment[ 0]
Segments0 =
new Segment(loadFactor, (int)(cap * loadFactor),
(HashEntry[])new HashEntry[cap]);
Segment[] ss = (Segment [])new Segment[ssize];
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}
Segment(float lf, int threshold, HashEntry
[] tab) {
this.loadFactor = lf;
this.threshold = threshold;
this.table = tab;
}
看完ConcurrentHashMap的构造函数,我们应该对该map的数据结构有个大致了解,如下:
put方法,首先对key值第一次hash确定segment的位置,然后在segment内部获取锁,接着key第二次hash,确定hashEntry在table中的位置,然后put操作和hashMap相同,最后关闭锁
public V put(K key, V value) {//找到对应的segment,吧key,value放入对应的segment中
Segments;
if (value == null)
throw new NullPointerException();
int hash = hash(key);
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);
return s.put(key, hash, value, false);
}
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
//每个segment进行put操作的时候都要进行加锁操作
HashEntrynode = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry[] tab = table;//table为segment所连接的hashEntry数组,
int index = (tab.length - 1) & hash;
HashEntryfirst = entryAt(tab, index);//找到数组对应下标的链表
for (HashEntrye = first;;) {
if (e != null) {//头结点不为空
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {//若是key相同则替换
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {//把当前node插入到table[i]的位置
if (node != null)
node.setNext(first);
else
node = new HashEntry(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
remove()操作
public V remove(Object key) {
int hash = hash(key);
Segments = segmentForHash(hash);//找到对应的segment
return s == null ? null : s.remove(key, hash, null);
}
final V remove(Object key, int hash, Object value) {
if (!tryLock())//获取锁
scanAndLock(key, hash);
V oldValue = null;
try {
HashEntry[] tab = table;//table是segment所连接的hashEntrt数组
int index = (tab.length - 1) & hash;
HashEntrye = entryAt(tab, index);//找到链表头
HashEntrypred = null;//记录删除节点的前一节点
while (e != null) {
K k;
HashEntrynext = e.next;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
V v = e.value;
if (value == null || value == v || value.equals(v)) {
if (pred == null)//删除头结点
setEntryAt(tab, index, next);
else
pred.setNext(next);
++modCount;
--count;
oldValue = v;
}
break;
}
pred = e;
e = next;
}
} finally {
unlock();
}
return oldValue;
}