/** * Creates a new, empty map with an initial table size based on * the given number of elements ({@code initialCapacity}), initial * table density ({@code loadFactor}), and number of concurrently * updating threads ({@code concurrencyLevel}). * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements, * given the specified load factor. * @param loadFactor the load factor (table density) for * establishing the initial table size * @param concurrencyLevel the estimated number of concurrently * updating threads. The implementation may use this value as * a sizing hint. * @throws IllegalArgumentException if the initial capacity is * negative or the load factor or concurrencyLevel are * nonpositive */ publicConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) thrownewIllegalArgumentException(); //初始容量不能小于并发级别 if (initialCapacity < concurrencyLevel) // Use at least as many bins initialCapacity = concurrencyLevel; // as estimated threads longsize= (long)(1.0 + (long)initialCapacity / loadFactor); intcap= (size >= (long)MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)size); this.sizeCtl = cap; }
/** * Maps the specified key to the specified value in this table. * Neither the key nor the value can be null. * * <p>The value can be retrieved by calling the {@code get} method * with a key that is equal to the original key. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with {@code key}, or * {@code null} if there was no mapping for {@code key} * @throws NullPointerException if the specified key or value is null */ public V put(K key, V value) { return putVal(key, value, false); }
/** Implementation for put and putIfAbsent */ final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) thrownewNullPointerException(); //spread 方法: //对键的哈希值进行重新计算,以减少哈希冲突。 //公式为:(h ^ (h >>> 16)) & HASH_BITS,其中 HASH_BITS 是一个掩码。 inthash= spread(key.hashCode()); intbinCount=0; for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; K fk; V fv; //如果tab为空,初始化tab,为了阅读的连贯性,我们后面再看这个方法 if (tab == null || (n = tab.length) == 0) tab = initTable(); //如果为空桶,使用casTabAt elseif ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, newNode<K,V>(hash, key, value))) break; // no lock when adding to empty bin } //这个MOVED其实是个特殊标记,表示当前正在进行扩容操作 //static final int MOVED = -1; // hash for forwarding nodes elseif ((fh = f.hash) == MOVED) //这个方法还是挺核心的,主要作用是检测当前是否正在进行扩容操作,并参与协助完成扩容任务。 //如果发现某个桶(bucket)已经被标记为正在迁移(通过 ForwardingNode 标记),则该方法会尝试参与到迁移过程中。 //它的主要目标是加速扩容过程,通过允许多个线程并行地完成数据迁移。 tab = helpTransfer(tab, f); elseif (onlyIfAbsent // check first node without acquiring lock && fh == hash && ((fk = f.key) == key || (fk != null && key.equals(fk))) && (fv = f.val) != null) return fv; else { //如果对应问题都没有出现,那就对f枷锁,然后把节点放入到对应的位置中去,这里的思路其实与HashMap一致 VoldVal=null; synchronized (f) { if (tabAt(tab, i) == f) { if (fh >= 0) { binCount = 1; for (Node<K,V> e = f;; ++binCount) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; if ((e = e.next) == null) { pred.next = newNode<K,V>(hash, key, value); break; } } } elseif (f instanceof TreeBin) { Node<K,V> p; binCount = 2; if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } elseif (f instanceof ReservationNode) thrownewIllegalStateException("Recursive update"); } } if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount); returnnull; }