ConcurrentHashMap源码剖析 (3)

这里“通话”和“重地”的哈希值是一样的,那么他们添加时,会存入同一个Segment对象,必然会存在锁竞争

public static void main(String[] args) throws Exception { final ConcurrentHashMap chm = new ConcurrentHashMap(); new Thread(){ @Override public void run() { chm.put("通话","11"); System.out.println("-----------"); } }.start(); //让第一个线程先启动,进入put方法 Thread.sleep(1000); new Thread(){ @Override public void run() { chm.put("重地","22"); System.out.println("==========="); } }.start(); }

断点设置

ConcurrentHashMap源码剖析

ConcurrentHashMap源码剖析

运行结果

会发现两个线程,分别停在不同的断点位置,这就是多线程锁互斥产生的结果

然后就可以分别让不同的线程向下执行,查看代码走向了。

ConcurrentHashMap源码剖析

1.4 jdk1.7扩容安全

源码分析

private void rehash(HashEntry<K,V> node) { HashEntry<K,V>[] oldTable = table; int oldCapacity = oldTable.length; //两倍容量 int newCapacity = oldCapacity << 1; threshold = (int)(newCapacity * loadFactor); //基于新容量,创建HashEntry数组 HashEntry<K,V>[] newTable = (HashEntry<K,V>[]) new HashEntry[newCapacity]; int sizeMask = newCapacity - 1; //实现数据迁移 for (int i = 0; i < oldCapacity ; i++) { HashEntry<K,V> e = oldTable[i]; if (e != null) { HashEntry<K,V> next = e.next; int idx = e.hash & sizeMask; if (next == null) // Single node on list //原位置只有一个元素,直接放到新数组即可 newTable[idx] = e; else { // Reuse consecutive sequence at same slot //=========图一===================== HashEntry<K,V> lastRun = e; int lastIdx = idx; for (HashEntry<K,V> last = next; last != null; last = last.next) { int k = last.hash & sizeMask; if (k != lastIdx) { lastIdx = k; lastRun = last; } } //=========图一===================== //=========图二===================== newTable[lastIdx] = lastRun; //=========图二===================== // Clone remaining nodes //=========图三===================== for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { V v = p.value; int h = p.hash; int k = h & sizeMask; HashEntry<K,V> n = newTable[k]; //这里旧的HashEntry不会放到新数组 //而是基于原来的数据创建了一个新的HashEntry对象,放入新数组 newTable[k] = new HashEntry<K,V>(h, p.key, v, n); } //=========图三===================== } } } //采用头插法,将新元素加入到数组中 int nodeIndex = node.hash & sizeMask; // add the new node node.setNext(newTable[nodeIndex]); newTable[nodeIndex] = node; table = newTable; }

图一

ConcurrentHashMap源码剖析

图二

ConcurrentHashMap源码剖析

图三

ConcurrentHashMap源码剖析

1.5 jdk1.7集合长度获取 public int size() { // Try a few times to get accurate count. On failure due to // continuous async changes in table, resort to locking. final Segment<K,V>[] segments = this.segments; int size; boolean overflow; // true if size overflows 32 bits long sum; // sum of modCounts long last = 0L; // previous sum int retries = -1; // first iteration isn't retry try { for (;;) { //当第5次走到这个地方时,会将整个Segment[]的所有Segment对象锁住 if (retries++ == RETRIES_BEFORE_LOCK) { for (int j = 0; j < segments.length; ++j) ensureSegment(j).lock(); // force creation } sum = 0L; size = 0; overflow = false; for (int j = 0; j < segments.length; ++j) { Segment<K,V> seg = segmentAt(segments, j); if (seg != null) { //累加所有Segment的操作次数 sum += seg.modCount; int c = seg.count; //累加所有segment中的元素个数 size+=c if (c < 0 || (size += c) < 0) overflow = true; } } //当这次累加值和上一次累加值一样,证明没有进行新的增删改操作,返回sum //第一次last为0,如果有元素的话,这个for循环最少循环两次的 if (sum == last) break; //记录累加的值 last = sum; } } finally { //如果之前有锁住,解锁 if (retries > RETRIES_BEFORE_LOCK) { for (int j = 0; j < segments.length; ++j) segmentAt(segments, j).unlock(); } } //溢出,返回int的最大值,否则返回累加的size return overflow ? Integer.MAX_VALUE : size; } 2. ConcurrentHashMap源码分析(JDK1.8) 2.1 jdk1.8容器初始化

在jdk8的ConcurrentHashMap中一共有5个构造方法,这四个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做了处理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsdsy.html