1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
| /* * Overview: * 综述: * * * The primary design goal of this hash table is to maintain * concurrent readability (typically method get(), but also * iterators and related methods) while minimizing update * contention. Secondary goals are to keep space consumption about * the same or better than java.util.HashMap, and to support high * initial insertion rates on an empty table by many threads. * * 这个哈希表的主要设计目标是维护并发可读性(通常是方法get(),也包括迭代器和相关方法),同时最小化更新争用。 * 次要目标是保持空间消耗与java.util相同或更好。并支持多个线程对空表的高初始插入率。 * * This map usually acts as a binned (bucketed) hash table. Each * key-value mapping is held in a Node. Most nodes are instances * of the basic Node class with hash, key, value, and next * fields. However, various subclasses exist: TreeNodes are * arranged in balanced trees, not lists. TreeBins hold the roots * of sets of TreeNodes. ForwardingNodes are placed at the heads * of bins during resizing. ReservationNodes are used as * placeholders while establishing values in computeIfAbsent and * related methods. The types TreeBin, ForwardingNode, and * ReservationNode do not hold normal user keys, values, or * hashes, and are readily distinguishable during search etc * because they have negative hash fields and null key and value * fields. (These special nodes are either uncommon or transient, * so the impact of carrying around some unused fields is * insignificant.) * * 这个映射通常充当一个桶哈希表。每个键值映射都保存在一个节点中。 * 大多数节点是具有散列、键、值和next字段的基本节点类的实例。 * 然而,存在各种子类:树节点被安排在平衡的树中,而不是列表中。 * TreeBins是拥有根节点的树节点。在调整大小时,ForwardingNodes被放置在箱子的顶部。 * ReservationNodes用作占位符,同时在computeIfAbsent和相关方法中建立值。 * TreeBin、forward节点和ReservationNode类型不包含普通的用户键、值或散列, * 在搜索过程中很容易区分,因为它们具有负散列字段和空键和值字段。 * (这些特殊节点不是不常见就是短暂的,因此携带一些未使用的字段的影响是微不足道的。) * * The table is lazily initialized to a power-of-two size upon the * first insertion. Each bin in the table normally contains a * list of Nodes (most often, the list has only zero or one Node). * Table accesses require volatile/atomic reads, writes, and * CASes. Because there is no other way to arrange this without * adding further indirections, we use intrinsics * (sun.misc.Unsafe) operations. * * 第一次插入时,该表被惰性地初始化为2的幂大小。表中的每个桶通常包含一个节点列表(大多数情况下,列表只有0个或一个节点)。 * 表访问需要volatile/atomic读写和CASes。 * 因为没有其他方法可以在不添加进一步间接的情况下安排此操作, * 所以我们使用了(sun.misc.Unsafe)操作。 * * We use the top (sign) bit of Node hash fields for control * purposes -- it is available anyway because of addressing * constraints. Nodes with negative hash fields are specially * handled or ignored in map methods. * * 我们使用节点哈希字段的顶部(符号)位进行控制 --无论如何,它都是可用的,因为要处理约束。 * 在map方法中,具有负哈希字段的节点被特殊处理或忽略。 * * Insertion (via put or its variants) of the first node in an * empty bin is performed by just CASing it to the bin. This is * by far the most common case for put operations under most * key/hash distributions. Other update operations (insert, * delete, and replace) require locks. We do not want to waste * the space required to associate a distinct lock object with * each bin, so instead use the first node of a bin list itself as * a lock. Locking support for these locks relies on builtin * "synchronized" monitors. * 将第一个节点插入到空容器中,只需通过CAS将其装箱即可。 * 到目前为止,这是大多数键/散列分布下put操作最常见的情况。 * 一些更新操作需要锁。我们不想浪费将一个不同的锁对象与每个bin关联所需的空间,所以应该使用bin列表本身的第一个节点作为锁。 * 对这些锁的锁定支持依赖于内置的“synchronized”监视器。 * * Using the first node of a list as a lock does not by itself * suffice though: When a node is locked, any update must first * validate that it is still the first node after locking it, and * retry if not. Because new nodes are always appended to lists, * once a node is first in a bin, it remains first until deleted * or the bin becomes invalidated (upon resizing). * * 使用列表的第一个节点作为锁本身还不够:当一个节点被锁定时,任何更新必须首先验证它仍然是锁定后的第一个节点,如果不是,则重试。 * 因为新节点总是被附加到列表中,一旦一个节点在一个bin中是第一个节点,它就会保持在第一个,直到删除或bin失效(调整大小后)。 * * The main disadvantage of per-bin locks is that other update * operations on other nodes in a bin list protected by the same * lock can stall, for example when user equals() or mapping * functions take a long time. However, statistically, under * random hash codes, this is not a common problem. Ideally, the * frequency of nodes in bins follows a Poisson distribution * (http://en.wikipedia.org/wiki/Poisson_distribution) with a * parameter of about 0.5 on average, given the resizing threshold * of 0.75, although with a large variance because of resizing * granularity. Ignoring variance, the expected occurrences of * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The * first values are: * * 0: 0.60653066 * 1: 0.30326533 * 2: 0.07581633 * 3: 0.01263606 * 4: 0.00157952 * 5: 0.00015795 * 6: 0.00001316 * 7: 0.00000094 * 8: 0.00000006 * more: less than 1 in ten million * * per-bin锁的主要缺点是,受同一锁保护的bin列表中的其他节点上的其他更新操作可能会停止,例如当user equals()或映射函数花费很长时间时。 * 然而,统计上,在随机哈希码下,这不是一个常见的问题。理想情况下,在给定调整大小阈值为0.75的情况下, * 箱中节点的频率遵循泊松分布(Poisson distribution),其参数平均约为0.5,尽管由于调整粒度而存在较大的方差。 * 忽略方差,列表大小k的预期出现次数为(exp(-0.5) * pow(0.5, k) / factorial(k))。 * 第一个值是: * 0: 0.60653066 * 1: 0.30326533 * 2: 0.07581633 * 3: 0.01263606 * 4: 0.00157952 * 5: 0.00015795 * 6: 0.00001316 * 7: 0.00000094 * 8: 0.00000006 * more: 少于千分之一 * * Lock contention probability for two threads accessing distinct * elements is roughly 1 / (8 * #elements) under random hashes. * 在随机散列下,两个线程访问不同元素的锁争用概率大约为1 /(8 * #elements)。 * * Actual hash code distributions encountered in practice * sometimes deviate significantly from uniform randomness. This * includes the case when N > (1<<30), so some keys MUST collide. * Similarly for dumb or hostile usages in which multiple keys are * designed to have identical hash codes or ones that differs only * in masked-out high bits. So we use a secondary strategy that * applies when the number of nodes in a bin exceeds a * threshold. These TreeBins use a balanced tree to hold nodes (a * specialized form of red-black trees), bounding search time to * O(log N). Each search step in a TreeBin is at least twice as * slow as in a regular list, but given that N cannot exceed * (1<<64) (before running out of addresses) this bounds search * steps, lock hold times, etc, to reasonable constants (roughly * 100 nodes inspected per operation worst case) so long as keys * are Comparable (which is very common -- String, Long, etc). * TreeBin nodes (TreeNodes) also maintain the same "next" * traversal pointers as regular nodes, so can be traversed in * iterators in the same way. * * 实际的哈希码分布在实践中有时会明显偏离均匀随机性。这包括当N >(1<<30)时,因此一些键必须碰撞。 * 类似地,在一些愚蠢或恶意的用法中,多个键被设计为具有相同的哈希码,或者只有在屏蔽的高位上不同的哈希码。 * 因此,当一个bin中的节点数量超过阈值时,我们使用一个辅助策略。 * 这些TreeBins使用一个平衡树来保存节点(红黑树的一种特殊形式),将搜索时间限制到O(log N)。 * 每个搜索一步TreeBin至少两倍慢在一个常规列表,但是鉴于N不能超过(1 < < 64)(在地址用完之前)这个界限搜索步骤,锁定持有时间,等, * 合理的常量(大约100节点检查每个操作最坏的情况)只要键具有可比性(这是很常见的,String, Long,等等)。 * TreeBin节点(TreeNodes)也像普通节点一样维护相同的“next”遍历指针,因此可以以相同的方式在迭代器中遍历。 * * The table is resized when occupancy exceeds a percentage * threshold (nominally, 0.75, but see below). Any thread * noticing an overfull bin may assist in resizing after the * initiating thread allocates and sets up the replacement array. * However, rather than stalling, these other threads may proceed * with insertions etc. The use of TreeBins shields us from the * worst case effects of overfilling while resizes are in * progress. Resizing proceeds by transferring bins, one by one, * from the table to the next table. However, threads claim small * blocks of indices to transfer (via field transferIndex) before * doing so, reducing contention. A generation stamp in field * sizeCtl ensures that resizings do not overlap. Because we are * using power-of-two expansion, the elements from each bin must * either stay at same index, or move with a power of two * offset. We eliminate unnecessary node creation by catching * cases where old nodes can be reused because their next fields * won't change. On average, only about one-sixth of them need * cloning when a table doubles. The nodes they replace will be * garbage collectable as soon as they are no longer referenced by * any reader thread that may be in the midst of concurrently * traversing table. Upon transfer, the old table bin contains * only a special forwarding node (with hash field "MOVED") that * contains the next table as its key. On encountering a * forwarding node, access and update operations restart, using * the new table. * * 当占有率超过百分比阈值(通常为0.75,但看情况)时,将调整表的大小。 * 任何线程注意到一个过满的bin都可以在初始化线程分配并设置替换数组之后帮助调整大小。 * 但是,这些其他线程可能会继续执行插入等操作,而不是停止。 * TreeBins的使用保护了我们从最坏的情况下,过量填充的影响,而调整的过程中。 * 调整大小的方法是将箱子一个一个地从表转移到下一个表。 * 然而,线程在这样做之前声明要传输一小块索引(通过字段transferIndex),从而减少争用。 * 字段sizeCtl中的生成戳记确保大小调整不会重叠。 * 因为我们使用的是2的幂展开,所以每个bin中的元素必须保持相同的索引,或者以2的幂偏移量移动。 * 我们通过捕获可以重用旧节点的情况来消除不必要的节点创建,因为它们的下一个字段不会更改。 * 平均而言,当表翻倍时,只有大约六分之一的表需要克隆。 * 它们替换的节点一旦不再被并发遍历表中的任何读线程引用,就会成为垃圾收集节点。 * 在传输时,旧表bin只包含一个特殊的转发节点(散列字段“MOVED”),该节点包含下一个表作为键。 * 遇到转发节点时,使用新表重新启动访问和更新操作。 * * * Each bin transfer requires its bin lock, which can stall * waiting for locks while resizing. However, because other * threads can join in and help resize rather than contend for * locks, average aggregate waits become shorter as resizing * progresses. The transfer operation must also ensure that all * accessible bins in both the old and new table are usable by any * traversal. This is arranged in part by proceeding from the * last bin (table.length - 1) up towards the first. Upon seeing * a forwarding node, traversals (see class Traverser) arrange to * move to the new table without revisiting nodes. To ensure that * no intervening nodes are skipped even when moved out of order, * a stack (see class TableStack) is created on first encounter of * a forwarding node during a traversal, to maintain its place if * later processing the current table. The need for these * save/restore mechanics is relatively rare, but when one * forwarding node is encountered, typically many more will be. * So Traversers use a simple caching scheme to avoid creating so * many new TableStack nodes. (Thanks to Peter Levart for * suggesting use of a stack here.) * * 每个bin传输都需要它的bin锁,它可以在调整大小时等待锁定。 * 但是,由于其他线程可以加入并帮助调整大小,而不是争用锁,所以随着调整大小的进行,平均聚合等待时间会变得更短。 * 传输操作还必须确保旧表和新表中所有可访问的箱子都可用于任何遍历。 * 这部分是通过从最后一个bin(table.length - 1)向上到第一个。 * 当看到一个转发节点时,遍历(参见类遍历器)会安排移动到新表,而不需要重新访问节点。 * 为了确保即使在无序移动时也不会跳过中间节点,在遍历过程中第一次遇到转发节点时会创建一个堆栈(请参见类TableStack), * 以便在以后处理当前表时维护其位置。对这些保存/恢复机制的需求相对较少,但当遇到一个转发节点时,通常会遇到更多。 * 因此遍历器使用一个简单的缓存方案来避免创建如此多的新表栈节点。(感谢Peter Levart建议在这里使用堆栈。) * * * The traversal scheme also applies to partial traversals of * ranges of bins (via an alternate Traverser constructor) * to support partitioned aggregate operations. Also, read-only * operations give up if ever forwarded to a null table, which * provides support for shutdown-style clearing, which is also not * currently implemented. * * 遍历方案还适用于桶范围的部分遍历(通过另一个遍历构造函数),以支持分区的聚合操作。 * 此外,如果将只读操作转发到空表,则将放弃只读操作,空表提供了对关闭样式清除的支持,而关闭样式清除目前也没有实现。 * * Lazy table initialization minimizes footprint until first use, * and also avoids resizings when the first operation is from a * putAll, constructor with map argument, or deserialization. * These cases attempt to override the initial capacity settings, * but harmlessly fail to take effect in cases of races. * * 延迟表初始化在第一次使用之前将占用的空间最小化,并且当第一个操作来自putAll、 * 带有map参数的构造函数或反序列化时,还可以避免调整大小。 * 这些情况试图覆盖初始容量设置,但在种族的情况下没有造成伤害。 * * The element count is maintained using a specialization of * LongAdder. We need to incorporate a specialization rather than * just use a LongAdder in order to access implicit * contention-sensing that leads to creation of multiple * CounterCells. The counter mechanics avoid contention on * updates but can encounter cache thrashing if read too * frequently during concurrent access. To avoid reading so often, * resizing under contention is attempted only upon adding to a * bin already holding two or more nodes. Under uniform hash * distributions, the probability of this occurring at threshold * is around 13%, meaning that only about 1 in 8 puts check * threshold (and after resizing, many fewer do so). * * 元素计数使用LongAdder的专门化来维护。为了隐式访问,我们需要合并专门化, * 而不是仅仅使用LongAdder竞争感应,导致多个对抗细胞的产生。 * 计数器机制可以避免更新上的争用,但是如果在并发访问期间读得太频繁,可能会遇到缓存抖动。 * 为了避免频繁读取,仅在添加到已经包含两个或多个节点的bin时才尝试在争用下调整大小。 * 在均匀哈希分布下,这种情况发生在阈值处的概率约为13%,这意味着只有大约八分之一的人设置了检查阈值(调整大小后,这样做的人更少)。 * * TreeBins use a special form of comparison for search and * related operations (which is the main reason we cannot use * existing collections such as TreeMaps). TreeBins contain * Comparable elements, but may contain others, as well as * elements that are Comparable but not necessarily Comparable for * the same T, so we cannot invoke compareTo among them. To handle * this, the tree is ordered primarily by hash value, then by * Comparable.compareTo order if applicable. On lookup at a node, * if elements are not comparable or compare as 0 then both left * and right children may need to be searched in the case of tied * hash values. (This corresponds to the full list search that * would be necessary if all elements were non-Comparable and had * tied hashes.) On insertion, to keep a total ordering (or as * close as is required here) across rebalancings, we compare * classes and identityHashCodes as tie-breakers. The red-black * balancing code is updated from pre-jdk-collections * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) * based in turn on Cormen, Leiserson, and Rivest "Introduction to * Algorithms" (CLR). * * TreeBins使用一种特殊的比较形式进行搜索和相关操作(这是我们不能使用现有集合(如TreeMaps)的主要原因)。 * TreeBins包含可比较的元素,但也可能包含其他元素,以及对相同的T具有可比性但不一定具有可比性的元素,因此我们不能在它们之间调用compareTo。 * 为了处理这个问题,树的顺序主要是按散列值排序,然后按Comparable.compareTo 排序,如果适用的话。 * 在节点查找时,如果元素不可比较或比较为0,那么在绑定哈希值的情况下,可能需要搜索左子元素和右子元素。 * (这对应于完整的列表搜索,如果所有元素都是不可比较的,并且已经绑定了散列,那么这将是必要的。) * 在插入时,为了保持跨重新平衡的总顺序(或尽可能接近这里的要求),我们将类和identityHashCodes作为连接符进行比较。 * 红黑平衡代码是根据Cormen、Leiserson和Rivest的“算法介绍”(CLR),从pre-jdk-collections更新的。 * * * TreeBins also require an additional locking mechanism. While * list traversal is always possible by readers even during * updates, tree traversal is not, mainly because of tree-rotations * that may change the root node and/or its linkages. TreeBins * include a simple read-write lock mechanism parasitic on the * main bin-synchronization strategy: Structural adjustments * associated with an insertion or removal are already bin-locked * (and so cannot conflict with other writers) but must wait for * ongoing readers to finish. Since there can be only one such * waiter, we use a simple scheme using a single "waiter" field to * block writers. However, readers need never block. If the root * lock is held, they proceed along the slow traversal path (via * next-pointers) until the lock becomes available or the list is * exhausted, whichever comes first. These cases are not fast, but * maximize aggregate expected throughput. * * TreeBins还需要一个额外的锁定机制。虽然即使在更新期间,读取器也始终可以执行列表遍历, * 但是树遍历不行,这主要是因为树的旋转可能会更改根节点和/或其链接。 * TreeBins包含一个简单的读写锁机制,寄生在主要的bin同步策略上: * 与插入或删除相关的结构调整已经锁定(因此不能与其他作者发生冲突),但必须等待正在进行的读者完成。 * 由于只能有一个这样的waiter,所以我们使用一个简单的方案,使用一个“waiter”字段来阻止编写器。 * 然而,读者永远不需要阻塞。如果持有根锁,它们将沿着缓慢的遍历路径(通过下一个指针)进行,直到锁可用或列表耗尽,无论哪个先出现。 * 这些情况并不快,但是可以最大限度地提高总预期吞吐量。 * * Maintaining API and serialization compatibility with previous * versions of this class introduces several oddities. Mainly: We * leave untouched but unused constructor arguments refering to * concurrencyLevel. We accept a loadFactor constructor argument, * but apply it only to initial table capacity (which is the only * time that we can guarantee to honor it.) We also declare an * unused "Segment" class that is instantiated in minimal form * only when serializing. * * 维护这个类以前版本的API和序列化兼容性会带来一些奇怪的现象。主要: * 我们保留未修改但未使用的构造函数参数来引用concurrencyLevel。 * 我们接受loadFactor构造函数参数,但只将其应用于初始表容量(这是我们能够保证遵守它的惟一时间)。 * 我们还声明了一个未使用的“Segment”类,该类仅在序列化时以最小形式实例化。 * * Also, solely for compatibility with previous versions of this * class, it extends AbstractMap, even though all of its methods * are overridden, so it is just useless baggage. * * 而且,仅为了与该类的以前版本兼容,它扩展了AbstractMap,即使覆盖了它的所有方法,所以它只是无用的包袱。 * * This file is organized to make things a little easier to follow * while reading than they might otherwise: First the main static * declarations and utilities, then fields, then main public * methods (with a few factorings of multiple public methods into * internal ones), then sizing methods, trees, traversers, and * bulk operations. * * 这个文件的组织是为了让阅读的时候更容易理解: * 首先是主静态声明和实用程序,然后是字段,然后是主公共方法(将多个公共方法分解为内部方法),然后是调整方法大小、树、遍历器和批量操作。 */
|