关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

ConcurrentHashMap

发布时间:2023-06-26 13:00:40

ConcurrentHashMap(JDK1.8)

  • 流程图:
  • ConcurrentHashMap属性列表

sizeCtl:表初始化和调整大小控制字段.如果为负数,则表正在初始化或调整大小,此时写操作将自旋

Node的Hash节点数据节点类型:MOVED=-1,列表的格式正在转换;TREEBIN=-2,树节点的Hash;RESERVED=-3,临时Hash;HASH_BITS=0x7fffffff,正常节点Hash

  • ConcurrentHashMap的putVal源码
final V putVal(K key, V value, boolean onlyIfAbsent) {  if (key == null || value == null) throw new NullPointerException();  //将key转换为hash码,尽可能的保证hash的均匀分布,且hash值必须大于0,因为小于0的是特殊hash  int hash = spread(key.hashCode());  int binCount = 0;  for (Node[] tab = table;;) {  Nodef; int n, i, fh;  //如果tab为空  if (tab == null || (n = tab.length) == 0)  //则创建table  tab = initTable();  //查询tab中第1个元素,如果为空,则将第一个元素直接插入  else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {  if (casTabAt(tab, i, null,  new Node(hash, key, value, null)))  break; // no lock when adding to empty bin  }  //hash获取,在JDK1.8中,ConcurrentHashMap中所有列表的第一个node作为锁  //内置了不同的hash码,来标记不同的状态,  //将节点的元素数据移植到新表上面  else if ((fh = f.hash) == MOVED)  tab = helpTransfer(tab, f);  else {  V oldVal = null;  //对节点f加锁  synchronized (f) {  //二次验证节点f  if (tabAt(tab, i) == f) {  //如果f节点的Hash值正常,也就是大于0  if (fh >= 0) {  binCount = 1;  for (Nodee = f;; ++binCount) {  K ek;  //如果存在hash一致的  if (e.hash == hash &&  ((ek = e.key) == key ||  (ek != null && key.equals(ek)))) {  oldVal = e.val;  //且允许覆盖,则覆盖  if (!onlyIfAbsent)  e.val = value;  break;  }  Nodepred = e;  //否则链表尾插入  if ((e = e.next) == null) {  pred.next = new Node(hash, key,  value, null);  break;  }  }  }  //如果节点f为树类型节点  else if (f instanceof TreeBin) {  Nodep;  binCount = 2;  if ((p = ((TreeBin)f).putTreeVal(hash, key,  value)) != null) {  oldVal = p.val;  if (!onlyIfAbsent)  p.val = value;  }  }  }  }  //如果链表节点个数大于8且table数据个数大于默认上限64,则转化为红黑树  //如果小于64,则只是尝试扩容  if (binCount != 0) {  if (binCount >= TREEIFY_THRESHOLD)  treeifyBin(tab, i);  if (oldVal != null)  return oldVal;  break;  }  }  }  addCount(1L, binCount);  return null;  } • ConcurrentHashMap#putVal • initTable()源码  private final Node[] initTable() {  Node[] tab; int sc;  while ((tab = table) == null || tab.length == 0) {  //获取系统的sizeCtl赋值给sc,如果sizeCtl小于0,则认为此时的数据已经被其余线程修改,自身进入等待......  if ((sc = sizeCtl) < 0)  Thread.yield(); // 当前线程自旋  //CAS操作  //获取当前对象的SIZECTL与sc比较,如果系统的SIZECTL等于sc,则将SIZECTL修改为-1  else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {  try {  if ((tab = table) == null || tab.length == 0) {  //为Table设置元素存取上限  int n = (sc > 0) ? sc : DEFAULT_CAPACITY;  @SuppressWarnings("unchecked")  Node[] nt = (Node[])new Node[n];  table = tab = nt;  sc = n - (n >>> 2);  }  } finally {  sizeCtl = sc;  }  break;  }  }  return tab;  }

   

  • Unsafe.compareAndSwapInt(Object var1, long var2, int var4, int var5);
    • 乐观锁CAS(比较交换):如果对象var1中的属性var2的值与期望值var4一致,则认为数据在内存没有被修改,此时把var5赋值给var2
    • CAS流程图:
    • 无锁CAS与锁的区别
      • 在性能方面:无锁CAS不需要线程之间切换的开销;但是无锁CAS可能会导致问题一致处于无解的状态,进而一直自旋,导致CPU资源一直占用
  • ConcurrentHashMap、 HashMap、Hashtable对比

ConcurrentHashMapHashMapHashtable
数据安全?安全不安全安全
性能?
  • HashTable通过synchronized关键字实现了线程安全,但是HashTable在方法级别上使用了synchronized,会使线程独占一张HashTable,性能较低
  • ConcurrentHashMap通过synchronized以及CAS来实现线程安全,但是ConcurrentHashMap是通过对数据节点使用了synchronized,其相对于HashTable来讲,封锁的粒度更低,可以保证线程不是独占HashTable,而是独占了某一部分数据,对于其余的数据依然可以访问,因此其性能相较于HashTable来说,性能更好

/template/Home/leiyu/PC/Static