集合

框架关系图

补充:HashTable父类是Dictionary,不是AbstractMap。

一:HashMap中的链循环:

一般来说HashMap中的链循环会发生在多线程操作时(虽然HashMap就不是线程安全的,一般多线程的时候也不会用它,但这种情况难免会发生)

以JDK1.7为例:

  1. void addEntry(int hash, K key, V value, int bucketIndex) {
  2. if ((size >= threshold) && (null != table[bucketIndex])) {
  3. resize(2 * table.length);//扩容方法,参数为原来数组长度的2倍
  4. hash = (null != key) ? hash(key) : 0;
  5. bucketIndex = indexFor(hash, table.length);
  6. }
  7.  
  8. createEntry(hash, key, value, bucketIndex);
  9. }
  10. void resize(int newCapacity) {
  11. Entry[] oldTable = table;//数组原来的数据
  12. int oldCapacity = oldTable.length;//原来数组的长度
  13. if (oldCapacity == MAXIMUM_CAPACITY) {
  14. threshold = Integer.MAX_VALUE;
  15. return;
  16. }
  17.  
  18. Entry[] newTable = new Entry[newCapacity];//以新的初始容量创建新的数组
  19. transfer(newTable, initHashSeedAsNeeded(newCapacity));//转换方法
  20. table = newTable;
  21. threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
  22. }
  23. void transfer(Entry[] newTable, boolean rehash) {
  24. int newCapacity = newTable.length;
  25. for (Entry<K,V> e : table) {
  26. while(null != e) {
  27. Entry<K,V> next = e.next;
  28. if (rehash) {
  29. e.hash = null == e.key ? 0 : hash(e.key);
  30. }
  31. int i = indexFor(e.hash, newCapacity);//e是oldTable上的Entry对象,这里算出新数组的下标
  32. e.next = newTable[i];
  33. newTable[i] = e;
  34. e = next;
  35. }
  36. }
  37. }

通过代码分析:当old Table中的数值都转移到newTable时,元素的顺序会进行颠倒。效果图如下:

在这种情况下,假设有两个线程同时进行扩容的操作且都进入了transfer方法时,但是因为某个情况让线程一先执行完,线程二才开始执行。

这个时候如果线程一执行完了,线程二开始的状态就是这样:

这个时候:线程二的e还是A对象,e.Next(A.Next)还是指向B,但是因为线程一的关系,实际上此时B.Next已经指向了A。于是就产生了循环:A.Next指向B,B.Next指向A。

二:hashtable

HashTable是Map接口下的一个子类,线程安全的,1.7和1.8差别不大,常和HashMap作比较,部分源码如下:

  1. //hashTable默认初始容量和加载因子
  2. public Hashtable() {
  3. this(11, 0.75f);
  4. }
  5.  
  6. //Put方法
  7. public synchronized V put(K key, V value) {
  8. // 不允许存null
  9. if (value == null) {
  10. throw new NullPointerException();
  11. }
  12.  
  13. // key相同时新值代替老值,并把老值返回出去
  14. Entry tab[] = table;
  15. int hash = hash(key);//哈希算法
  16. int index = (hash & 0x7FFFFFFF) % tab.length;
  17. for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
  18. if ((e.hash == hash) && e.key.equals(key)) {
  19. V old = e.value;
  20. e.value = value;
  21. return old;
  22. }
  23. }
  24.  
  25. modCount++;
  26. if (count >= threshold) {
  27. rehash();//扩容
  28.  
  29. tab = table;
  30. hash = hash(key);
  31. index = (hash & 0x7FFFFFFF) % tab.length;
  32. }
  33.  
  34. Entry<K,V> e = tab[index];
  35. tab[index] = new Entry<>(hash, key, value, e);
  36. count++;
  37. return null;
  38. }
  39. //哈希算法
  40. private int hash(Object k) {
  41. return hashSeed ^ k.hashCode();
  42. }
  43. //扩容方法
  44. protected void rehash() {
  45. int oldCapacity = table.length;
  46. Entry<K,V>[] oldMap = table;
  47.  
  48. int newCapacity = (oldCapacity << 1) + 1;//扩容后容量为原来的2倍再加1
  49. if (newCapacity - MAX_ARRAY_SIZE > 0) {
  50. if (oldCapacity == MAX_ARRAY_SIZE)
  51. // Keep running with MAX_ARRAY_SIZE buckets
  52. return;
  53. newCapacity = MAX_ARRAY_SIZE;
  54. }
  55. Entry<K,V>[] newMap = new Entry[newCapacity];
  56.  
  57. modCount++;
  58. threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
  59. boolean rehash = initHashSeedAsNeeded(newCapacity);
  60.  
  61. table = newMap;
  62.  
  63. for (int i = oldCapacity ; i-- > 0 ;) {
  64. for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
  65. Entry<K,V> e = old;
  66. old = old.next;
  67.  
  68. if (rehash) {
  69. e.hash = hash(e.key);
  70. }
  71. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  72. e.next = newMap[index];
  73. newMap[index] = e;
  74. }
  75. }
  76. }
  77. //remove方法
  78. public synchronized V remove(Object key) {
  79. Entry tab[] = table;
  80. int hash = hash(key);
  81. int index = (hash & 0x7FFFFFFF) % tab.length;
  82. for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
  83. if ((e.hash == hash) && e.key.equals(key)) {
  84. modCount++;
  85. if (prev != null) {
  86. prev.next = e.next;
  87. } else {
  88. tab[index] = e.next;
  89. }
  90. count--;
  91. V oldValue = e.value;
  92. e.value = null;
  93. return oldValue;
  94. }
  95. }
  96. return null;
  97. }
  98. //get方法
  99. public synchronized V get(Object key) {
  100. Entry tab[] = table;
  101. int hash = hash(key);
  102. int index = (hash & 0x7FFFFFFF) % tab.length;
  103. for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
  104. if ((e.hash == hash) && e.key.equals(key)) {
  105. return e.value;
  106. }
  107. }
  108. return null;
  109. }
  110. //keySet方法
  111. public Set<K> keySet() {
  112. if (keySet == null)
  113. keySet = Collections.synchronizedSet(new KeySet(), this);
  114. return keySet;
  115. }
  116.  
  117. private class KeySet extends AbstractSet<K> {
  118. public Iterator<K> iterator() {
  119. return getIterator(KEYS);
  120. }
  121. public int size() {
  122. return count;
  123. }
  124. public boolean contains(Object o) {
  125. return containsKey(o);
  126. }
  127. public boolean remove(Object o) {
  128. return Hashtable.this.remove(o) != null;
  129. }
  130. public void clear() {
  131. Hashtable.this.clear();
  132. }
  133. }
  134. //entrySet方法
  135. public Set<Map.Entry<K,V>> entrySet() {
  136. if (entrySet==null)
  137. entrySet = Collections.synchronizedSet(new EntrySet(), this);
  138. return entrySet;
  139. }
  140.  
  141. private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
  142. public Iterator<Map.Entry<K,V>> iterator() {
  143. return getIterator(ENTRIES);
  144. }
  145.  
  146. public boolean add(Map.Entry<K,V> o) {
  147. return super.add(o);
  148. }
  149.  
  150. public boolean contains(Object o) {
  151. if (!(o instanceof Map.Entry))
  152. return false;
  153. Map.Entry entry = (Map.Entry)o;
  154. Object key = entry.getKey();
  155. Entry[] tab = table;
  156. int hash = hash(key);
  157. int index = (hash & 0x7FFFFFFF) % tab.length;
  158.  
  159. for (Entry e = tab[index]; e != null; e = e.next)
  160. if (e.hash==hash && e.equals(entry))
  161. return true;
  162. return false;
  163. }
  164. //clear方法
  165. public synchronized void clear() {
  166. Entry tab[] = table;
  167. modCount++;
  168. for (int index = tab.length; --index >= 0; )
  169. tab[index] = null;
  170. count = 0;
  171. }
  172. //size方法和isEmpty方法
  173. public synchronized int size() {
  174. return count;
  175. }
  176.  
  177. public synchronized boolean isEmpty() {
  178. return count == 0;
  179. }

可以看出和HashMap比较,有以下特点:

1、初始容量为11。

2、扩容方法为:oldTable.Length*2+1。

3、不允许存null值。

4、计算hash,和获取数组下标不同:

HashTable:

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

HashMap:

int hash = hash(key);//
int i = indexFor(hash, table.length);

5、HashTable线程安全,主要的方法都用了synchronized,加锁。

6、HashMap和HashTable父类不同

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable

7、HashTable线程安全,但因为每个主要方法上都加上了锁,所以在执行效率上会慢很多。

三:其他线程安全Map

1、synchronizedMap

通过工具类Collections的方法,返回一个HashMap,例如:Map<String,String> map = Collections.synchronizedMap(new HashMap<String,String>());

  1. public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
  2. return new SynchronizedMap<>(m);
  3. }
  4.  
  5. private static class SynchronizedMap<K,V>
  6. implements Map<K,V>, Serializable {
  7. private static final long serialVersionUID = 1978198479659022715L;
  8.  
  9. private final Map<K,V> m; // Backing Map
  10. final Object mutex; // Object on which to synchronize
  11.  
  12. SynchronizedMap(Map<K,V> m) {
  13. if (m==null)
  14. throw new NullPointerException();
  15. this.m = m;
  16. mutex = this;
  17. }
  18.  
  19. SynchronizedMap(Map<K,V> m, Object mutex) {
  20. this.m = m;
  21. this.mutex = mutex;
  22. }
  23.  
  24. public int size() {
  25. synchronized (mutex) {return m.size();}
  26. }
  27. public boolean isEmpty() {
  28. synchronized (mutex) {return m.isEmpty();}
  29. }
  30. public boolean containsKey(Object key) {
  31. synchronized (mutex) {return m.containsKey(key);}
  32. }
  33. public boolean containsValue(Object value) {
  34. synchronized (mutex) {return m.containsValue(value);}
  35. }
  36. public V get(Object key) {
  37. synchronized (mutex) {return m.get(key);}
  38. }
  39.  
  40. public V put(K key, V value) {
  41. synchronized (mutex) {return m.put(key, value);}
  42. }
  43. public V remove(Object key) {
  44. synchronized (mutex) {return m.remove(key);}
  45. }
  46. public void putAll(Map<? extends K, ? extends V> map) {
  47. synchronized (mutex) {m.putAll(map);}
  48. }
  49. public void clear() {
  50. synchronized (mutex) {m.clear();}
  51. }
  52.  
  53. private transient Set<K> keySet = null;
  54. private transient Set<Map.Entry<K,V>> entrySet = null;
  55. private transient Collection<V> values = null;
  56.  
  57. public Set<K> keySet() {
  58. synchronized (mutex) {
  59. if (keySet==null)
  60. keySet = new SynchronizedSet<>(m.keySet(), mutex);
  61. return keySet;
  62. }
  63. }
  64.  
  65. public Set<Map.Entry<K,V>> entrySet() {
  66. synchronized (mutex) {
  67. if (entrySet==null)
  68. entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
  69. return entrySet;
  70. }
  71. }
  72.  
  73. public Collection<V> values() {
  74. synchronized (mutex) {
  75. if (values==null)
  76. values = new SynchronizedCollection<>(m.values(), mutex);
  77. return values;
  78. }
  79. }
  80.  
  81. public boolean equals(Object o) {
  82. if (this == o)
  83. return true;
  84. synchronized (mutex) {return m.equals(o);}
  85. }
  86. public int hashCode() {
  87. synchronized (mutex) {return m.hashCode();}
  88. }
  89. public String toString() {
  90. synchronized (mutex) {return m.toString();}
  91. }
  92. private void writeObject(ObjectOutputStream s) throws IOException {
  93. synchronized (mutex) {s.defaultWriteObject();}
  94. }
  95. }

可以看出,原理就是在原来Map的基础上,加上锁synchronized (mutex),来让线程变得安全,但和HashTable一样,效率慢。

2、ConcurrentHashMap

JDK1.7中的ConcurrentHashMap:

ConcurrentHashMap是线程安全的Map,继承AbstractMap。是和HashTable把整合数组共享一把锁不同,ConcurrentHashMap采用分段的思想,把HashMap分为多个段进行加锁的操作,这样既保证了线程安全性,又不会使效率像HashTable那样低。

ConcurrentHashMap的特点是分段式加锁,并利用Unsafe对象和volatile关键字来实现线程安全,他比较明显的一个局限性是并发级别一旦指定就不再更改。

结构:

代码:

  1. //默认初始容量(2的幂),实际上是指的HashEntry的数量
  2. static final int DEFAULT_INITIAL_CAPACITY = 16;
  3. //默认加载因子
  4. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  5. //默认并发级别(实际上指的Segmengt对象的数量,初始化后不可更改)
  6. static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  7. //最大容量小于等于2的30次幂
  8. static final int MAXIMUM_CAPACITY = 1 << 30;
  9. //最小SEGMENT容量(一个Segment里至少有两个HashEntry)
  10. static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
  11. //最大SEGMENT容量容量
  12. static final int MAX_SEGMENTS = 1 << 16;
  13. //加锁之前的重试次数
  14. static final int RETRIES_BEFORE_LOCK = 2;
  15. //Segment数组
  16. final Segment<K,V>[] segments;
  17. //计算segment位置的掩码,用于计算Segment[]下标
  18. final int segmentMask;
  19. //用于算segment位置时,hash参与运算的位数
  20. final int segmentShift;
  21. //内部类HashEntry,相当于HashMap中Entry[]中的Entry对象
  22. static final class HashEntry<K,V> {
  23. final int hash;
  24. final K key;
  25. volatile V value;
  26. volatile HashEntry<K,V> next;
  27.  
  28. HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
  29. this.hash = hash;
  30. this.key = key;
  31. this.value = value;
  32. this.next = next;
  33. }
  34.  
  35. final void setNext(HashEntry<K,V> n) {
  36. UNSAFE.putOrderedObject(this, nextOffset, n);
  37. }
  38.  
  39. static final sun.misc.Unsafe UNSAFE;
  40. static final long nextOffset;
  41. static {
  42. try {
  43. UNSAFE = sun.misc.Unsafe.getUnsafe();
  44. Class k = HashEntry.class;
  45. nextOffset = UNSAFE.objectFieldOffset
  46. (k.getDeclaredField("next"));
  47. } catch (Exception e) {
  48. throw new Error(e);
  49. }
  50. }
  51. }
  52. //hash函数
  53. private int hash(Object k) {
  54. int h = hashSeed;
  55.  
  56. if ((0 != h) && (k instanceof String)) {
  57. return sun.misc.Hashing.stringHash32((String) k);
  58. }
  59.  
  60. h ^= k.hashCode();
  61.  
  62. h += (h << 15) ^ 0xffffcd7d;
  63. h ^= (h >>> 10);
  64. h += (h << 3);
  65. h ^= (h >>> 6);
  66. h += (h << 2) + (h << 14);
  67. return h ^ (h >>> 16);
  68. }
  69. //ConcurrentHashMap的构造方法
  70. public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {//初始容量,加载因子,并发级别
  71. if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)//保证参数不能小于0
  72. throw new IllegalArgumentException();
  73. if (concurrencyLevel > MAX_SEGMENTS)//保证并发级别不能大于最大Segment容量
  74. concurrencyLevel = MAX_SEGMENTS;
  75.  
  76. int sshift = 0;
  77. int ssize = 1;
  78. while (ssize < concurrencyLevel) {
  79. ++sshift;//统计ssize左移的次数
  80. ssize <<= 1;//初始是1,通过左移然后2、4、8、16...作用是找到大于等于并发级别的2的“sshift”次幂
  81. }
  82. this.segmentShift = 32 - sshift;//用于Put方法中,求Segment[]数组的下标时用
  83. this.segmentMask = ssize - 1;//Segmengt[].length-1
  84. if (initialCapacity > MAXIMUM_CAPACITY)//如果初始容量大于最大
  85. initialCapacity = MAXIMUM_CAPACITY;//则选择最大容量作为容量
  86. int c = initialCapacity / ssize;//HashEntry[].length/Segmengt[].length,用来标记HashEntry的数量
  87. if (c * ssize < initialCapacity)//例如initialCapacity = 9,concurrencyLevel(ssize) = 8 ,则initialCapacity / ssize = 1,c * ssize < initialCapacity
  88. ++c; // 然后让C = 2, 此时 c * ssize >= initialCapacity ,这里的目的就是保证数据都被Segmengt存储。
  89. int cap = MIN_SEGMENT_TABLE_CAPACITY;
  90. while (cap < c)//拿最小的容量和刚刚计算的 c 进行比较
  91. cap <<= 1; // 保证最小容量是2的n次幂
  92. Segment<K,V> s0 =
  93. new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
  94. (HashEntry<K,V>[])new HashEntry[cap]);//初始化Segment对象
  95. Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];//初始化Segment数组
  96. UNSAFE.putOrderedObject(ss, SBASE, s0); //调用Unsafe中的方法,作用是把刚刚初始化的Segemengt对象s0,放到刚刚初始化Segment[]数组中的Segment[0],也就是第一位
  97. this.segments = ss;
  98. }
  99. //Put方法
  100. public V put(K key, V value) {
  101. Segment<K,V> s;
  102. if (value == null)
  103. throw new NullPointerException();//不能传null,此处个人认为应该加个 key也不能为 null的判断
  104. int hash = hash(key);//计算hash值
  105. int j = (hash >>> segmentShift) & segmentMask; // 计算Segmengt[]下标,即数据存储的位置
  106. if ((s = (Segment<K,V>)UNSAFE.getObject(segments, (j << SSHIFT) + SBASE)) == null) // 调用Unsafe的方法,作用是获取Segmengt[j]的对象,如果等于空
  107. s = ensureSegment(j);//判断第j个位置是不是为空,保证线程安全性
  108. return s.put(key, hash, value, false);//调用Segmengt对象的put方法
  109. }
  110.  
  111. //Segmengt的Put方法
  112. final V put(K key, int hash, V value, boolean onlyIfAbsent) {
  113. HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);//加锁,保证线程安全
  114. V oldValue;
  115. try {
  116. HashEntry<K,V>[] tab = table;
  117. int index = (tab.length - 1) & hash;
  118. HashEntry<K,V> first = entryAt(tab, index);//获取该位置的第一个元素,然后遍历链表
  119. for (HashEntry<K,V> e = first;;) {
  120. if (e != null) {
  121. K k;
  122. if ((k = e.key) == key ||
  123. (e.hash == hash && key.equals(k))) {
  124. oldValue = e.value;
  125. if (!onlyIfAbsent) {
  126. e.value = value;
  127. ++modCount;
  128. }
  129. break;
  130. }
  131. e = e.next;
  132. }
  133. else {
  134. if (node != null)
  135. node.setNext(first);
  136. else
  137. node = new HashEntry<K,V>(hash, key, value, first);
  138. int c = count + 1;
  139. if (c > threshold && tab.length < MAXIMUM_CAPACITY)
  140. rehash(node);
  141. else
  142. setEntryAt(tab, index, node);
  143. ++modCount;
  144. count = c;
  145. oldValue = null;
  146. break;
  147. }
  148. }
  149. } finally {
  150. unlock();
  151. }
  152. return oldValue;
  153. }
  154.  
  155. @SuppressWarnings("unchecked")
  156. private void rehash(HashEntry<K,V> node) {//扩容方法,只扩容HashEntry[],没扩容Segmengt[]
  157.  
  158. HashEntry<K,V>[] oldTable = table;
  159. int oldCapacity = oldTable.length;
  160. int newCapacity = oldCapacity << 1;
  161. threshold = (int)(newCapacity * loadFactor);
  162. HashEntry<K,V>[] newTable =
  163. (HashEntry<K,V>[]) new HashEntry[newCapacity];
  164. int sizeMask = newCapacity - 1;
  165. for (int i = 0; i < oldCapacity ; i++) {
  166. HashEntry<K,V> e = oldTable[i];
  167. if (e != null) {
  168. HashEntry<K,V> next = e.next;
  169. int idx = e.hash & sizeMask;
  170. if (next == null)
  171. newTable[idx] = e;
  172. else {
  173. HashEntry<K,V> lastRun = e;
  174. int lastIdx = idx;
  175. for (HashEntry<K,V> last = next;
  176. last != null;
  177. last = last.next) {
  178. int k = last.hash & sizeMask;
  179. if (k != lastIdx) {
  180. lastIdx = k;
  181. lastRun = last;
  182. }
  183. }
  184. newTable[lastIdx] = lastRun;
  185.  
  186. for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
  187. V v = p.value;
  188. int h = p.hash;
  189. int k = h & sizeMask;
  190. HashEntry<K,V> n = newTable[k];
  191. newTable[k] = new HashEntry<K,V>(h, p.key, v, n);
  192. }
  193. }
  194. }
  195. }
  196. int nodeIndex = node.hash & sizeMask;
  197. node.setNext(newTable[nodeIndex]);
  198. newTable[nodeIndex] = node;
  199. table = newTable;
  200. }
  201.  
  202. private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
  203. HashEntry<K,V> first = entryForHash(this, hash);
  204. HashEntry<K,V> e = first;
  205. HashEntry<K,V> node = null;
  206. int retries = -1;
  207. while (!tryLock()) {
  208. HashEntry<K,V> f;
  209. if (retries < 0) {
  210. if (e == null) {
  211. if (node == null)
  212. node = new HashEntry<K,V>(hash, key, value, null);
  213. retries = 0;
  214. }
  215. else if (key.equals(e.key))
  216. retries = 0;
  217. else
  218. e = e.next;
  219. }
  220. else if (++retries > MAX_SCAN_RETRIES) {
  221. lock();
  222. break;
  223. }
  224. else if ((retries & 1) == 0 &&
  225. (f = entryForHash(this, hash)) != first) {
  226. e = first = f;
  227. retries = -1;
  228. }
  229. }
  230. return node;
  231. }
  232.  
  233. private void scanAndLock(Object key, int hash) {
  234.  
  235. HashEntry<K,V> first = entryForHash(this, hash);
  236. HashEntry<K,V> e = first;
  237. int retries = -1;
  238. while (!tryLock()) {
  239. HashEntry<K,V> f;
  240. if (retries < 0) {
  241. if (e == null || key.equals(e.key))
  242. retries = 0;
  243. else
  244. e = e.next;
  245. }
  246. else if (++retries > MAX_SCAN_RETRIES) {
  247. lock();
  248. break;
  249. }
  250. else if ((retries & 1) == 0 &&
  251. (f = entryForHash(this, hash)) != first) {
  252. e = first = f;
  253. retries = -1;
  254. }
  255. }
  256. }
  257.  
  258. final V remove(Object key, int hash, Object value) {
  259. if (!tryLock())
  260. scanAndLock(key, hash);
  261. V oldValue = null;
  262. try {
  263. HashEntry<K,V>[] tab = table;
  264. int index = (tab.length - 1) & hash;
  265. HashEntry<K,V> e = entryAt(tab, index);
  266. HashEntry<K,V> pred = null;
  267. while (e != null) {
  268. K k;
  269. HashEntry<K,V> next = e.next;
  270. if ((k = e.key) == key ||
  271. (e.hash == hash && key.equals(k))) {
  272. V v = e.value;
  273. if (value == null || value == v || value.equals(v)) {
  274. if (pred == null)
  275. setEntryAt(tab, index, next);
  276. else
  277. pred.setNext(next);
  278. ++modCount;
  279. --count;
  280. oldValue = v;
  281. }
  282. break;
  283. }
  284. pred = e;
  285. e = next;
  286. }
  287. } finally {
  288. unlock();
  289. }
  290. return oldValue;
  291. }
  292.  
  293. final boolean replace(K key, int hash, V oldValue, V newValue) {
  294. if (!tryLock())
  295. scanAndLock(key, hash);
  296. boolean replaced = false;
  297. try {
  298. HashEntry<K,V> e;
  299. for (e = entryForHash(this, hash); e != null; e = e.next) {
  300. K k;
  301. if ((k = e.key) == key ||
  302. (e.hash == hash && key.equals(k))) {
  303. if (oldValue.equals(e.value)) {
  304. e.value = newValue;
  305. ++modCount;
  306. replaced = true;
  307. }
  308. break;
  309. }
  310. }
  311. } finally {
  312. unlock();
  313. }
  314. return replaced;
  315. }
  316.  
  317. final V replace(K key, int hash, V value) {
  318. if (!tryLock())
  319. scanAndLock(key, hash);
  320. V oldValue = null;
  321. try {
  322. HashEntry<K,V> e;
  323. for (e = entryForHash(this, hash); e != null; e = e.next) {
  324. K k;
  325. if ((k = e.key) == key ||
  326. (e.hash == hash && key.equals(k))) {
  327. oldValue = e.value;
  328. e.value = value;
  329. ++modCount;
  330. break;
  331. }
  332. }
  333. } finally {
  334. unlock();
  335. }
  336. return oldValue;
  337. }
  338.  
  339. final void clear() {
  340. lock();
  341. try {
  342. HashEntry<K,V>[] tab = table;
  343. for (int i = 0; i < tab.length ; i++)
  344. setEntryAt(tab, i, null);
  345. ++modCount;
  346. count = 0;
  347. } finally {
  348. unlock();
  349. }
  350. }
  351. }
  352. //Get方法
  353. public V get(Object key) {
  354. Segment<K,V> s;
  355. HashEntry<K,V>[] tab;
  356. int h = hash(key);
  357. long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;//先获取Segment[] 的下标
  358. if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { //用Unsafe的方法获取到Segment对象
  359. //用Unsafe的方法HashEntry对象,然后遍历链表
  360. for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); e != null; e = e.next) {
  361. K k;
  362. if ((k = e.key) == key || (e.hash == h && key.equals(k)))
  363. return e.value;
  364. }
  365. }
  366. return null;
  367. }

JDK1.8:

JDK1.8时,ConcurrentHashMap又放弃了分段式加锁的思想,而且也不在用Segment[]数组存值,而是采用在HashMap1.8的基础上,采用Node[] + 链表 + 红黑树 + CAS+ Synchronized 的思想保证线程安全。而且在扩容的时候,不仅要满足链表结构大于8,还要满足数据容量大于64。

  1. //最大容量
  2. private static final int MAXIMUM_CAPACITY = 1 << 30;
  3. //默认容量
  4. private static final int DEFAULT_CAPACITY = 16;
  5. //最大数组长度
  6. static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  7. //默认并发等级(1.7遗留,兼容以前版本)
  8. private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  9. //加载因子(1.7遗留,兼容以前版本)
  10. private static final float LOAD_FACTOR = 0.75f;
  11. //转换为红黑树的阈值(道理和HashMap1.8中一样,这里指链表长度)
  12. static final int TREEIFY_THRESHOLD = 8;
  13. //红黑树反转成链表的阈值
  14. static final int UNTREEIFY_THRESHOLD = 6;
  15. //转换为红黑树的阈值(这里指数组长度)
  16. static final int MIN_TREEIFY_CAPACITY = 64;
  17. //扩容转移时的最小数组分组大小
  18. private static final int MIN_TRANSFER_STRIDE = 16;
  19. //本类中没提供修改的方法 用来根据n生成位置一个类似时间搓的功能
  20. private static int RESIZE_STAMP_BITS = 16;
  21. // 2^15-1,help resize的最大线程数
  22. private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
  23. // 32-16=16,sizeCtl中记录size大小的偏移量
  24. private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
  25. //表示正在转移
  26. static final int MOVED = -1;
  27. // 表示已转换为红黑树
  28. static final int TREEBIN = -2;
  29. // 保留
  30. static final int RESERVED = -3;
  31. // 用在计算hash时进行安位与计算消除负hash
  32. static final int HASH_BITS = 0x7fffffff;
  33. // 可用处理器数量
  34. static final int NCPU = Runtime.getRuntime().availableProcessors();
  35. //构造函数
  36. public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
  37. if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)//判断参数是否大于等于0
  38. throw new IllegalArgumentException();
  39. if (initialCapacity < concurrencyLevel)
  40. initialCapacity = concurrencyLevel; // 容量最小为16
  41. long size = (long)(1.0 + (long)initialCapacity / loadFactor);
  42. int cap = (size >= (long)MAXIMUM_CAPACITY) ?
  43. MAXIMUM_CAPACITY : tableSizeFor((int)size);//获取数组容量并保证不大于最大容量
  44. this.sizeCtl = cap;
  45. }
  46. //Put方法
  47. public V put(K key, V value) {
  48. return putVal(key, value, false);
  49. }
  50.  
  51. final V putVal(K key, V value, boolean onlyIfAbsent) {
  52. if (key == null || value == null) throw new NullPointerException();//不允许存null
  53. int hash = spread(key.hashCode());//计算hash值
  54. int binCount = 0;
  55. for (Node<K,V>[] tab = table;;) {
  56. Node<K,V> f; int n, i, fh;
  57. if (tab == null || (n = tab.length) == 0)
  58. tab = initTable();//懒加载,数组为空时初始化
  59. else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//类似Unsafe获取对象的方法获取对象
  60. if (casTabAt(tab, i, null,
  61. new Node<K,V>(hash, key, value, null)))//CAS,如果获取位置为空,则将数据存入
  62. break;
  63. }
  64. else if ((fh = f.hash) == MOVED)//首地址处不null 并且Node的hash是-1 表示是ForwardingNode节点正在rehash扩容
  65. tab = helpTransfer(tab, f);//帮助扩容的方法
  66. else {
  67. V oldVal = null;
  68. synchronized (f) {//加锁
  69. if (tabAt(tab, i) == f) {
  70. if (fh >= 0) {
  71. binCount = 1;
  72. for (Node<K,V> e = f;; ++binCount) {
  73. K ek;
  74. if (e.hash == hash &&
  75. ((ek = e.key) == key ||
  76. (ek != null && key.equals(ek)))) {//遍历链表,如果key重复,值替换,老值返回出去
  77. oldVal = e.val;
  78. if (!onlyIfAbsent)
  79. e.val = value;
  80. break;
  81. }
  82. Node<K,V> pred = e;
  83. if ((e = e.next) == null) {
  84. pred.next = new Node<K,V>(hash, key,
  85. value, null);
  86. break;
  87. }
  88. }
  89. }
  90. else if (f instanceof TreeBin) {//如果为红黑树的对象,调用红黑树的put方法
  91. Node<K,V> p;
  92. binCount = 2;
  93. if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
  94. value)) != null) {
  95. oldVal = p.val;
  96. if (!onlyIfAbsent)
  97. p.val = value;
  98. }
  99. }
  100. }
  101. }
  102. if (binCount != 0) {
  103. if (binCount >= TREEIFY_THRESHOLD)
  104. //如果链表数据超过指定阈值,转换红黑树,并且会再进行一次判断,看数组容量是否大于64,数组大于64后才会转换为红黑树
  105. treeifyBin(tab, i);
  106. if (oldVal != null)
  107. return oldVal;
  108. break;
  109. }
  110. }
  111. }
  112. addCount(1L, binCount);
  113. return null;
  114. }
  115. //Get方法,基本和1.8HashMap一样,获取数组坐标,然后获取Node对象,并且没有加锁。
  116. public V get(Object key) {
  117. Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
  118. int h = spread(key.hashCode());
  119. if ((tab = table) != null && (n = tab.length) > 0 &&
  120. (e = tabAt(tab, (n - 1) & h)) != null) {
  121. if ((eh = e.hash) == h) {
  122. if ((ek = e.key) == key || (ek != null && key.equals(ek)))
  123. return e.val;
  124. }
  125. else if (eh < 0)
  126. return (p = e.find(h, key)) != null ? p.val : null;
  127. while ((e = e.next) != null) {
  128. if (e.hash == h &&
  129. ((ek = e.key) == key || (ek != null && key.equals(ek))))
  130. return e.val;
  131. }
  132. }
  133. return null;
  134. }

注意:分析ConcurrentHashMap源码前,需要先了解一下CAS、Unsafe、Synchronized 、Volatile相关知识。

数据结构(集合)学习之Map(二)的更多相关文章

  1. 数据结构(集合)学习之Map(一)

    集合 框架关系图: 补充:HashTable父类是Dictionary,不是AbstractMap. Map: Map(接口)和Collection都属于集合,但是Map不是Collection的子类 ...

  2. 数据结构(集合)学习之Set

    集合 框架关系图: Collection接口下面有三个子接口:List.Set.Queue.此篇是关于Set<E>的简单学习总结. 补充:HashTable父类是Dictionary,不是 ...

  3. 数据结构(集合)学习之List

    集合 框架关系图: Collection接口下面有三个子接口:List.Set.Queue.此篇是关于List<E>的简单学习总结. 补充:HashTable父类是Dictionary,不 ...

  4. 数据结构(集合)学习之Queue

    集合 框架关系图: Collection接口下面有三个子接口:List.Set.Queue.此篇是关于Queue<E>的简单学习总结. 补充:HashTable父类是Dictionary, ...

  5. 数据结构(集合)学习之Collection和Iterator

    集合 1.集合与数组 数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用. 集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使 ...

  6. 2019/3/4 java集合学习(二)

    java集合学习(二) 在学完ArrayList 和 LinkedList之后,基本已经掌握了最基本的java常用数据结构,但是为了提高程序的效率,还有很多种特点各异的数据结构等着我们去运用,类如可以 ...

  7. 【转】Java学习---Java核心数据结构(List,Map,Set)使用技巧与优化

    [原文]https://www.toutiao.com/i6594587397101453827/ Java核心数据结构(List,Map,Set)使用技巧与优化 JDK提供了一组主要的数据结构实现, ...

  8. Java学习:集合双列Map

    数据结构 数据结构: 数据结构_栈:先进后出 入口和出口在同一侧 数据结构_队列:先进先出 入口和出口在集合的两侧 数据结构_数组: 查询快:数组的地址是连续的,我们通过数组的首地址可以找到数组,通过 ...

  9. java集合学习(2):Map和HashMap

    Map接口 java.util 中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map. Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含 ...

随机推荐

  1. 删除我的电脑wps、百度网盘图标

    删除我的电脑wps.百度网盘图标 删除下面子项 输入"计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Expl ...

  2. 05讲基础篇:某个应用的CPU使用率居然达到100%,我该怎么办

    小结 CPU 使用率是最直观和最常用的系统性能指标,更是我们在排查性能问题时,通常会关注的第一个指标.所以我们更要熟悉它的含义,尤其要弄清楚用户(%user).Nice(%nice).系统(%syst ...

  3. js笔记(3)--js实现数组转置(两种方法)

      js实现数组转置   第一种方法:   <script>     window.onload=function(){     var array1=[[11,22,33,333],[4 ...

  4. 设计模式——Adapter Pattern 适配器模式

    我第一次接触设计模式,选取了四大类型里面的结构型,这类型的特点是关注类&对象之间的组合(使用继承),我从中选取适配器模式来具体学习. 一.适配器模式(Adapter Pattern)定义: 适 ...

  5. Python爬虫小结

    有些数据是没有专门的数据集的,为了找到神经网络训练的数据,自然而然的想到了用爬虫的方法开始采集数据.一开始采用了网上的一个动态爬虫的代码,发现爬取的图片大多是重复的,有效图片很少. 动态爬虫: fro ...

  6. c++ 初始化列表和构造函数初始化区别

    先上代码 #include <iostream> class MyContruct { public: MyContruct() { std::cout << "My ...

  7. Robot Framework自动化测试框架核心指南-如何使用Java编写自定义的RobotFramework Lib

    如何使用Java编写自定义的RobotFramework Lib 本文包括2个章节 1. Robot Frdamwork中如何调用java Lib库 2.使用 java编写自定义的Lib 本文作者为: ...

  8. MySQL8.0 InnoDB并行执行

    概述 MySQL经过多年的发展已然成为最流行的数据库,广泛用于互联网行业,并逐步向各个传统行业渗透.之所以流行,一方面是其优秀的高并发事务处理的能力,另一方面也得益于MySQL丰富的生态.MySQL在 ...

  9. CSS选择器世界

    CSS选择器世界 CSS选择器的分类与优先级 css选择器分为四类:选择器.选择符(后代关系的空格.>.+.~.||).伪类.伪元素(::before.::after.::first-lette ...

  10. Django请求过程