Java并发包探秘 (二) ConcurrentHashMap(四)

2014-11-24 01:45:23 · 作者: · 浏览: 7
oldValue = v;

// All entries following removed node can stay

// in list, but all preceding ones need to be

// cloned.

++modCount;

HashEntry newFirst = e.next;

for (HashEntry p = first; p != e; p = p.next)

newFirst = new HashEntry(p.key, p.hash,

newFirst, p.value);//2.注意看这里,就是逐个复制。

tab[index] = newFirst;

count = c; // write-volatile 3.这里依然是JSR 166 保证线程间可见性。

}

}

return oldValue;

} finally {

unlock();

}

}

在删除之前依然是:

Java代码

public V remove(Object key) {

int hash = hash(key.hashCode());

return segmentFor(hash).remove(key, hash, null);

}

其中get方法和remove方法都调用了一个segmentFor()方法,其实并发HashMap的所有操作都和这个方法有关。到底是如何实现的呢?

Java代码

/**

* Returns the segment that should be used for key with given hash

* @param hash the hash code for the key

* @return the segment

*/

final Segment segmentFor(int hash) {

return segments[(hash >>> segmentShift) & segmentMask];

}

需要特别注意一下segmentShift和segmentMask,假设构造函数确定了Segment的数量是2的n次方,那么segmentShift就等于32减去n,而segmentMask就等于2的n次方减一。一般最常见的值是Segment数量2^4 = 16,segmentShift=28,segmentMask=15,经过segmentFor()运算就能快速的定位到具体的Segment上面。

Java代码

public V put(K key, V value) {

if (value == null)

throw new NullPointerException();

int hash = hash(key.hashCode());

return segmentFor(hash).put(key, hash, value, false);

}

public V putIfAbsent(K key, V value) {

if (value == null)

throw new NullPointerException();

int hash = hash(key.hashCode());

return segmentFor(hash).put(key, hash, value, true);

}

put方法和putIfAbsent方法都调用了Segment的put(etc...)方法,仅仅只是最后一个参数不同:

Java代码

V put(K key, int hash, V value, boolean onlyIfAbsent) {

lock();

try {

int c = count;

if (c++ > threshold) // ensure capacity

rehash();

HashEntry[] tab = table;

int index = hash & (tab.length - 1);

HashEntry first = tab[index];

HashEntry e = first;

while (e != null && (e.hash != hash || !key.equals(e.key)))

e = e.next;

V oldValue;

if (e != null) {

oldValue = e.value;

if (!onlyIfAbsent)//不同之处,在这里体现。

e.value = value;

}

else {

oldValue = null;

++modCount;

tab[index] = new HashEntry(key, hash, first, value);

count = c; // write-volatile 最后写volatile变量,保证可见性

}

return oldValue;

} finally {

unlock();

}

}

并发HashMap的put和putIfAbsent的区别就是一个是直接放入并替换,另一个是有就不替换。其它方法诸如clear(),replace()等,都是在细粒度的锁下完成的。这里不做更细的讨论,详见并发HashMap