ConcurrentHashmap在JDK8代码解释 [英] ConcurrentHashmap in JDK8 code explanation

查看:176
本文介绍了ConcurrentHashmap在JDK8代码解释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在试图理解JDK8中的ConcurrentHashMap函数,与之相反的是JDK7(除了源代码,可以找到很好的解释很好的一些很好的人,如理查德 http://www.burnison.ca/articles/the-concurrency-of-concurrenthashmap )。它看起来像在JDK8中有相当多的改变 - 例如。没有更多的'段'本身,但不知何故我有一种感觉,这些更改意味着使代码更简单?


  1. 我在理解方法ConcurrentHashMap.putVal(...)时遇到一些困难,这个直接锁定在'segment'列表的头部插入else {}?:

      else if ((fh = f.hash)== MOVED)
    tab = helpTransfer(tab,f);
    else {// ...}


  2. ConcurrentHashMap.casTabAt(...)。


  3. 此外,关于JDK8中的ConcurrentHashMap.get(Object key)的源代码,它是严格没有锁定(我没有看到任何,如果是,它如何工作没有锁,因为我没有看到一个循环try-again吗?)还是有一些乐观的锁,我不遵守?


感谢某人可以提供一些提示。

关于 putVal(K键,V值,boolean onlyIfAbsent)方法
$ b

每个bin / bucket包含一个 hash 字段,它以非常聪明的方式结合了两个目的:




  • 对于常规bin(大多数只包含单个项的bin),它存储映射到此键的哈希码。

  • 对于特殊的bin(目前有3种类型),它包含一个特殊的负值。聪明的部分是,你只需要顶部位来区分正值和负值,因此正常bin从特殊bin。



本节

  else if((fh = f.hash)== MOVED)
tab = helpTransfer(tab,f);
else {// ...}

是找到地图后的第一个检查不为空,并且您尝试映射的键的bin不为空。



如果您发现的bin是其中一个特殊类型的箱 - 转发箱。转发bin是必需的,因为调整大小是同时并且迭代地完成的,并且已经转移(到新表)条目仍然需要可访问(通过旧表中的转发bin)。



关于 casTabAt((Node< K,V> [] tab,int i,Node< K,V> c,Node< K,V& c>方法



casTabAt()方法用于使用compare你可以看到典型的CAS循环在几乎所有使用 casTabAt()的地方 - 构造你想要放置的对象然后尝试CAS在它的正确的地方如果一个复杂的建筑可以在CAS尝试之前感到奇怪,你可以看看Jeff Preshing的你可以做任何类型的原子读 - 修改 - 写操作



在某种意义上, ConcurrentHashMap 仍然使用条带锁定,但是具有更精细的锁粒度(竞争区域现在从多bin段

关于 get(Object key)方法



get()方法可以没有任何锁定,大多数情况下,使用 volatile 语义(通过上述 casTabAt()方法和相关 tabAt()方法)。如果bin包含映射到同一个bin的条目的红黑树,那么情况就很棘手,你可以看到访问的 TreeBin 中的遍历总是完成的在同步块中。


I have been trying to understand the ConcurrentHashMap functions in JDK8, in contrast of how it was in JDK7 (which, in addition to the source code, can be found explained quite well by some nice folks out there such as Richard http://www.burnison.ca/articles/the-concurrency-of-concurrenthashmap). It looks like have been changed quite a bit in JDK8 - e.g. there is no more 'segment' per se, but somehow I got a feeling that the changes are meant to make the code simpler?

  1. I'm having some difficulty to understand the method ConcurrentHashMap.putVal(...), especially the following section - is this straight-forward locking on the head of the 'segment' list anyway for insertion in the else{}?:

        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {//...}
    

  2. Not so sure about code of the ConcurrentHashMap.casTabAt(...) either.

  3. Also, about the source code of ConcurrentHashMap.get(Object key) in JDK8, is it strictly no lock at all (I'm not seeing any, if so, how does it work without lock as I don't see a loop 'try-again' either?) or there is some kind of optimistic lock that I'm not observing?

Appreciate if someone could offer some hint.

解决方案

About the putVal(K key, V value, boolean onlyIfAbsent) method

Each bin/bucket contains a hash field, which combines two purposes in a very clever way:

  • For regular bins (most bins containing just a single item), it stores the hash code of the mapped here key. The top bit is cleared though (it's always set to 0).
  • For special bins (currently there are 3 types of these), it contains a special negative value. The clever part is that you need just the top bit to distinguish positive from negative values and therefore regular bins from special bins. Distinguishing between the different types of special bins is free to use the remaining 31 bits.

This section

else if ((fh = f.hash) == MOVED)
    tab = helpTransfer(tab, f);
else {//...}

is the first check after finding that the map is not empty and that the bin for the key you're trying to map is not empty.

It's satisfied in case the bin that you've found is one of the special types of bins - a forwarding bin. Forwarding bins are required because resizing is done concurrently and iteratively and already transferred (to the new table) entries still need to be accessible (through the forwarding bin in the old table).

About the casTabAt((Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) method

The casTabAt() method is used to atomically set a map entry using compare-and-swap operation for object references. You can still see the typical CAS loop in practically all places where casTabAt() is used - you construct the object that you want to put and then try to CAS it in its rightful place. If it feels weird that a complex construction can precede the CAS attempt, you can take a look at Jeff Preshing's You Can Do Any Kind of Atomic Read-Modify-Write Operation.

In a sense, ConcurrentHashMap still uses striped locking, but with finer lock granularity (the contended area is now minimized from multi-bin segments to individual bins) and with locks being almost entirely replaced by CAS operations.

About the get(Object key) method

The get() method can get away without any locking, because in most cases the bin contents are being set and retrieved using volatile semantics (through the aforementioned casTabAt() method and the related tabAt() method). The situation is trickier in case the bin contains a red-black tree of entries that were mapped to the same bin, and you can see that traversal within the accessed TreeBin is always done in a synchronized block.

这篇关于ConcurrentHashmap在JDK8代码解释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆