ConcurrentHashMap源码详解

今天药忘吃喽~ 2022-05-18 08:59 331阅读 0赞
成员变量
  1. private static final int MAXIMUM_CAPACITY = 1 << 30;
  2. private static final int DEFAULT_CAPACITY = 16;
  3. static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  4. private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  5. private static final float LOAD_FACTOR = 0.75f;
  6. static final int TREEIFY_THRESHOLD = 8;
  7. static final int UNTREEIFY_THRESHOLD = 6;
  8. static final int MIN_TREEIFY_CAPACITY = 64;
  9. private static final int MIN_TRANSFER_STRIDE = 16;
  10. private static int RESIZE_STAMP_BITS = 16;
  11. private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
  12. private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
  13. static final int MOVED = -1; // hash for forwarding nodes
  14. static final int TREEBIN = -2; // hash for roots of trees
  15. static final int RESERVED = -3; // hash for transient reservations
  16. static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
  17. //存储key-value的Node节点
  18. static class Node<K,V> implements Map.Entry<K,V> {
  19. final int hash;
  20. final K key;
  21. volatile V val;
  22. volatile Node<K,V> next;
  23. Node(int hash, K key, V val, Node<K,V> next) {
  24. this.hash = hash;
  25. this.key = key;
  26. this.val = val;
  27. this.next = next;
  28. }
  29. }
  30. // 初始化发生在第一次插入操作,默认大小为16的数组,用来存储Node节点数据,扩容时大小总是2的幂次方。
  31. transient volatile Node<K,V>[] table;
  32. // 默认为null,扩容时新生成的数组,其大小为原数组的两倍。
  33. private transient volatile Node<K,V>[] nextTable;
  34. private transient volatile long baseCount;
  35. // sizeCtl默认为0,用来控制table的初始化和扩容操作
  36. /*
  37. -1 代表table正在初始化
  38. -N 表示有N-1个线程正在进行扩容操作
  39. 另外:
  40. 1 如果table未初始化,表示table需要初始化的大小
  41. 2 如果table初始化完成,表示table的容量,默认是table大小的0.75倍
  42. */
  43. private transient volatile int sizeCtl;
  44. private transient volatile int transferIndex;
  45. private transient volatile int cellsBusy;
  46. private transient volatile CounterCell[] counterCells;
  47. private transient KeySetView<K,V> keySet;
  48. private transient ValuesView<K,V> values;
  49. private transient EntrySetView<K,V> entrySet;
  50. //下面三个方法为原子操作,分别对应volatile的取值,CAS操作,赋值
  51. static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
  52. return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
  53. }
  54. static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
  55. Node<K,V> c, Node<K,V> v) {
  56. return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
  57. }
  58. static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
  59. U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
  60. }
构造方法初始化
  1. //创建一个对象,什么都不做
  2. public ConcurrentHashMap() {
  3. }
  4. //传入容量值,需要将这个值变为比这个值大的2^n
  5. public ConcurrentHashMap(int initialCapacity) {
  6. if (initialCapacity < 0)
  7. throw new IllegalArgumentException();
  8. int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
  9. MAXIMUM_CAPACITY :
  10. tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
  11. this.sizeCtl = cap;
  12. }
  13. public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
  14. this.sizeCtl = DEFAULT_CAPACITY;
  15. putAll(m);
  16. }
  17. public ConcurrentHashMap(int initialCapacity, float loadFactor) {
  18. this(initialCapacity, loadFactor, 1);
  19. }
  20. public ConcurrentHashMap(int initialCapacity,
  21. float loadFactor, int concurrencyLevel) {
  22. if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
  23. throw new IllegalArgumentException();
  24. if (initialCapacity < concurrencyLevel) // Use at least as many bins
  25. initialCapacity = concurrencyLevel; // as estimated threads
  26. long size = (long)(1.0 + (long)initialCapacity / loadFactor);
  27. int cap = (size >= (long)MAXIMUM_CAPACITY) ?
  28. MAXIMUM_CAPACITY : tableSizeFor((int)size);
  29. this.sizeCtl = cap;
  30. }
table初始化
  1. //table的初始化不在构造方法中发生,而是在第一次put时,调用了initTable方法
  2. private final Node<K,V>[] initTable() {
  3. Node<K,V>[] tab;
  4. int sc;
  5. while ((tab = table) == null || tab.length == 0) {
  6. //如果一个线程发现sizeCtl<0,意味着另外的线程执行CAS操作成功,当前线程需要让出cpu时间片,重新进入就绪状态
  7. if ((sc = sizeCtl) < 0)
  8. Thread.yield(); // lost initialization race; just spin
  9. else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
  10. //sizeCtl未被设置过-1的值,则需要在本线程中创建对象
  11. try {
  12. //首先通过CAS将sizeCtl置为-1,这样别的线程就知道这个线程在创建对象了
  13. if ((tab = table) == null || tab.length == 0) {
  14. int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
  15. @SuppressWarnings("unchecked")
  16. Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
  17. table = tab = nt;
  18. sc = n - (n >>> 2);//n-(n/4) 16-16/4=12,是不是记起了什么?
  19. }
  20. } finally {
  21. sizeCtl = sc;
  22. }
  23. break;
  24. }
  25. }
  26. return tab;
  27. }
put方法
  1. public V put(K key, V value) {
  2. return putVal(key, value, false);
  3. }
  4. /** Implementation for put and putIfAbsent */
  5. final V putVal(K key, V value, boolean onlyIfAbsent) {
  6. if (key == null || value == null) throw new NullPointerException();
  7. int hash = spread(key.hashCode());
  8. int binCount = 0;
  9. for (Node<K,V>[] tab = table;;) {
  10. Node<K,V> f; int n, i, fh;
  11. if (tab == null || (n = tab.length) == 0)
  12. tab = initTable();//初始化table
  13. else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
  14. //f为拿到的最新的元素值
  15. // 如果f为空的话,说明table中这个位置第一次插入元素,利用Unsafe.compareAndSwapObject方法插入Node节点
  16. if (casTabAt(tab, i, null,
  17. new Node<K,V>(hash, key, value, null)))
  18. break; // no lock when adding to empty bin
  19. }
  20. else if ((fh = f.hash) == MOVED) // 如果hash值为-1,说明正在进行扩容操作
  21. tab = helpTransfer(tab, f);//数据迁移代码不太容易,日后再说
  22. else {
  23. //f 是该位置的头结点,而且不为空
  24. V oldVal = null;
  25. // 其余情况把新的Node节点按链表或红黑树的方式插入到合适的位置,这个过程采用同步内置锁实现并发
  26. synchronized (f) {
  27. //在节点f上进行同步,节点插入之前,再次利用tabAt(tab, i) == f判断,防止被其它线程修改
  28. if (tabAt(tab, i) == f) {
  29. if (fh >= 0) {
  30. //链表
  31. binCount = 1;
  32. //遍历链表
  33. for (Node<K,V> e = f;; ++binCount) {
  34. K ek;
  35. if (e.hash == hash &&
  36. ((ek = e.key) == key ||
  37. (ek != null && key.equals(ek)))) {
  38. oldVal = e.val;
  39. if (!onlyIfAbsent)
  40. e.val = value;
  41. break;
  42. }
  43. // 到了链表的最末端,将这个新值放到链表的最后面
  44. Node<K,V> pred = e;
  45. if ((e = e.next) == null) {
  46. pred.next = new Node<K,V>(hash, key,
  47. value, null);
  48. break;
  49. }
  50. }
  51. }
  52. else if (f instanceof TreeBin) {
  53. //红黑树
  54. Node<K,V> p;
  55. binCount = 2;
  56. // 调用红黑树的插值方法插入新节点
  57. if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
  58. value)) != null) {
  59. oldVal = p.val;
  60. if (!onlyIfAbsent)
  61. p.val = value;
  62. }
  63. }
  64. }
  65. }
  66. if (binCount != 0) {
  67. if (binCount >= TREEIFY_THRESHOLD)
  68. //满足某些条件的时候,转化为红黑树
  69. treeifyBin(tab, i);
  70. if (oldVal != null)
  71. return oldVal;
  72. break;
  73. }
  74. }
  75. }
  76. addCount(1L, binCount);
  77. return null;
  78. }
get方法
  1. public V get(Object key) {
  2. Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
  3. int h = spread(key.hashCode());//得到hash值
  4. //取值操作不需要考虑并发,依据HashMap的实现就可以了
  5. if ((tab = table) != null && (n = tab.length) > 0 &&
  6. (e = tabAt(tab, (n - 1) & h)) != null) {
  7. if ((eh = e.hash) == h) {
  8. if ((ek = e.key) == key || (ek != null && key.equals(ek)))
  9. return e.val;
  10. }
  11. else if (eh < 0)
  12. return (p = e.find(h, key)) != null ? p.val : null;
  13. while ((e = e.next) != null) {
  14. if (e.hash == h &&
  15. ((ek = e.key) == key || (ek != null && key.equals(ek))))
  16. return e.val;
  17. }
  18. }
  19. return null;
  20. }

发表评论

表情:
评论列表 (有 0 条评论,331人围观)

还没有评论,来说两句吧...

相关阅读