ConcurrentHashMap1.7和ConcurrentHashMap1.8的异同
Posted 牛牛最爱喝兽奶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentHashMap1.7和ConcurrentHashMap1.8的异同相关的知识,希望对你有一定的参考价值。
ConcurrentHashMap
ConcurrentHashMap在与HashMap的不同之处在于多线程下更安全,而针对于Hashtable的基础上进行了一次升级。Hashtable的安全机制过于粗暴,导致性能很低。根据不同版本采用的方式也有不同。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable
1.7版本的ConcurrentHashMap
主要采用的是分段锁机制,并且读取时不进行加锁操作。底层的结构还是和1.7版本的hashmap类似,只是多了一个Segments数组,一个Segment可以看作类似的hashtable,里面包含了一个table数组用来存取hashentry,table里每个元素代表着链表的头节点,transient表示该变量不进行序列化,继承了ReentrantLock 重入锁。
static final class Segment<K,V> extends ReentrantLock implements Serializable {
transient volatile int count;
transient int modCount;
transient int threshold;
transient volatile HashEntry<K,V>[] table;
final float loadFactor;
}
HashEntry的声明类,用于数据存取的对象
static final class HashEntry<K,V> {
final K key; //键
final int hash; //hash值hashcode(k)^hashcode(v)
volatile V value;
final HashEntry<K,V> next; //final修饰的变量只能被初始化一次
}
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;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
segmentShift = 32 - sshift;
segmentMask = ssize - 1;
this.segments = Segment.newArray(ssize);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = 1;
while (cap < c)
cap <<= 1;
for (int i = 0; i < this.segments.length; ++i)
this.segments[i] = new Segment<K,V>(cap, loadFactor);
}
获取数据V不需要进行加锁,首先找到哪一个segment,然后确定table[i]数组中的位置,在进行遍历链表,取值。
1 public V get(Object key) {
2 int hash = hash(key.hashCode()); //采用二次hash减少hash冲突
3 return segmentFor(hash).get(key, hash);
4 }
final Segment<K,V> segmentFor(int hash) {
return segments[(hash >>> segmentShift) & segmentMask];
}
1 V get(Object key, int hash) { //get方法
2 if (count != 0) { // read-volatile // ①
3 HashEntry<K,V> e = getFirst(hash); 获取table里的元素
4 while (e != null) {
5 if (e.hash == hash && key.equals(e.key)) {
6 V v = e.value;
7 if (v != null) // ② 注意这里
8 return v;
9 return readValueUnderLock(e); // recheck
10 }
11 e = e.next;
12 }
13 }
14 return null;
15 }
在这里需要注意的是next指向下一个结点被final关键字修饰,也就是说不能在直接进行插入或者删除的工作,而且添加元素也只能在头部进行插入。当我们正在查询时,此时进行了添加工作,应该优先进行添加工作,happen-before原则。这样能保证我们读取的值是最新的。在创建entry时会发现很多变量都被volatile和final修饰,volatile主要是保证线程的有序性和可见性。
put操作源码
1 V put(K key, int hash, V value, boolean onlyIfAbsent) {
2 lock(); //获取锁的对象
3 try {
4 int c = count;
5 if (c++ > threshold) // ensure capacity 扩容
6 rehash();
7 HashEntry<K,V>[] tab = table;
8 int index = hash & (tab.length - 1); //获取数组下标值
9 HashEntry<K,V> first = tab[index]; //数组中第一个entry对象
10 HashEntry<K,V> e = first;
11 while (e != null && (e.hash != hash || !key.equals(e.key)))
12 e = e.next;
13
14 V oldValue;
15 if (e != null) { //表示数组已经存在该KEY
16 oldValue = e.value;
17 if (!onlyIfAbsent) //表示是否要覆盖
18 e.value = value;
19 }
20 else { //不存在key,
21 oldValue = null;
22 ++modCount;
23 tab[index] = new HashEntry<K,V>(key, hash, first, value);
24 count = c; // write-volatile
25 }
26 return oldValue;
27 } finally {
28 unlock();
29 }
30 }
remove操作详情
1 V remove(Object key, int hash, Object value) {
2 lock(); //获取锁对象
3 try {
4 int c = count - 1;
5 HashEntry<K,V>[] tab = table;
6 int index = hash & (tab.length - 1);
7 HashEntry<K,V> first = tab[index];
8 HashEntry<K,V> e = first;
9 while (e != null && (e.hash != hash || !key.equals(e.key)))
10 e = e.next;
11
12 V oldValue = null;
13 if (e != null) {
14 V v = e.value;
15 if (value == null || value.equals(v)) {
16 oldValue = v;
17 // All entries following removed node can stay
18 // in list, but all preceding ones need to be
19 // cloned.
20 ++modCount;
21 HashEntry<K,V> newFirst = e.next;
22 for (HashEntry<K,V> p = first; p != e; p = p.next)
23 newFirst = new HashEntry<K,V>(p.key, p.hash,
24 newFirst, p.value); //方法里的newFirst表示next指针。
25 tab[index] = newFirst;
26 count = c; // write-volatile
27 }
28 }
29 return oldValue;
30 } finally {
31 unlock(); //解锁释放锁的对象
32 }
33 }
由于删除之后表结构会发生该表,会将删除结点之前的元素进行一个倒叙排列,最后再将结点之后的元素连接起来。
ConcurrentHashMap1.8
ConcurrentHashMap1.8底层采用的数据结构是数组+链表+红黑树,采用的synchronization+CAS实现多线程安全机制。
Node结点代码里只有find并没有其他方法!
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;
}
}
一些compareAndSwapObject(CAS)操作,保证了原子性
@SuppressWarnings("unchecked")
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);
}
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);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
定义了一些变量
transient volatile Node<K,V>[] table;
/**
* The next table to use; non-null only while resizing.
*/
private transient volatile Node<K,V>[] nextTable;
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
*/
private transient volatile long baseCount;
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
*/
private transient volatile int sizeCtl;
/**
* The next table index (plus one) to split while resizing.
*/
private transient volatile int transferIndex;
/**
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
*/
private transient volatile int cellsBusy;
/**
* Table of counter cells. When non-null, size is a power of 2.
*/
private transient volatile CounterCell[] counterCells;
// views
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
get方法,并没有进行加锁,支持高并发
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) ConcurrentHashMap1.7和ConcurrentHashMap1.8的异同
ConcurrentHashMap1.7和ConcurrentHashMap1.8的异同