HashTable 写操作时候,Lock全表
源码:
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
因为该方法是用了synchronized方法,因此,可以防止多个线程同时方法这个对象的synchronized方法
对象中 remove,putAll,clear等常用方法都是用了synchronized 方法,因此,当一个线程调用其中的一个synchronized方法时,其他线程就不能方法对象中其中的任何一个synchronized方法。
ConcurrentHashMap 允许多个修改操作并发进行
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table。
/**
* The segments, each of which is a specialized hash table
*/
final Segment[] segments;
一个ConcurrentHashMap中默认有16个Segment段,源码如下:
/**
* The default number of concurrency control segments.
**/
static final int DEFAULT_SEGMENTS = 16;
现在看一下put 方法,源码
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key);
return segmentFor(hash).put(key, hash, value, false);
}
可见方法中不支持value为null的情况,如果为空则抛出空指针异常
操作时通过segmentFor方法得到 Segment[]中的Segment<K,V>,然后将KEY和VALUE存入segment中
再看一下Segment<K,V>对象的put方法
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<K,V> first = (HashEntry<K,V>) tab[index];
HashEntry<K,V> 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 { //不存在创建新的HashEntry保存数据
oldValue = null;
++modCount;
tab[index] = new HashEntry<K,V>(key, hash, first, value);
count = c; // write-volatile
}
return oldValue;
} finally {
unlock(); //释放锁
}
结论图: