Java8 API的源码中文备注版
扫码获取(付费,介意忽略)
/**** 支持完全并发检索和高度并发更新的哈希表。该类遵守与{@link java.util.Hashtable}相同的功能规范,并包括与{@code Hashtable}的每个方法对应的方法版本。然而,尽管所有操作都是线程安全的,检索操作不涉及锁定,并且没有任何支持以防止所有访问的方式锁定整个表。该类与依赖于其线程安全性但不依赖于其同步细节的程序中的{@code Hashtable}完全可互操作。** <p>检索操作(包括{@code get})通常不会阻塞,因此可能与更新操作(包括{@code put}和{@code remove})重叠。检索反映了最近完成的更新操作在其开始时保持的结果。(更正式地说,给定键的更新操作与报告更新值的任何(非空)检索之间存在一个“happens-before”关系。)对于诸如{@code putAll}和{@code clear}之类的聚合操作,同时进行的检索可能反映了仅插入或删除某些条目。同样,迭代器、分割器和枚举器返回反映哈希表在迭代器/枚举器创建时或之后某个时间点的状态的元素。它们不会抛出{@link java.util.ConcurrentModificationException ConcurrentModificationException}。然而,迭代器设计成一次只能由一个线程使用。请记住,聚合状态方法(包括{@code size}、{@code isEmpty}和{@code containsValue})的结果通常只在映射在其他线程中不进行并发更新时有用。否则,这些方法的结果反映了可能足以用于监视或估计目的但不适用于程序控制的瞬态状态。
* <p>当发生太多冲突时(即具有不同哈希码但落入相同槽位的键),表会动态扩展,预期平均效果是每个映射维持大约两个存储桶(对应于0.75的负载因子阈值进行调整大小)。由于映射的添加和删除可能会导致很大的方差,但总体上,这维持了散列表的常见时间/空间权衡。然而,调整此类或任何其他类型的散列表可能是一个相对较慢的操作。如果可能的话,最好提供一个大小估计作为可选的{@code initialCapacity}构造函数参数。另一个可选的{@code loadFactor}构造函数参数通过指定用于计算为给定元素数量分配空间量的表密度来进一步定制初始表容量。此外,为了与此类的先前版本兼容,构造函数可以可选地指定预期的{@code concurrencyLevel}作为内部大小的附加提示。请注意,使用许多具有完全相同的{@code hashCode()}的键是减慢任何散列表性能的一种确定方法。为了缓解影响,当键是{@link Comparable}时,此类可以使用键之间的比较顺序来帮助打破平局。** <p>可以创建ConcurrentHashMap的{@link Set}投影(使用{@link #newKeySet()}或{@link #newKeySet(int)}),或者查看(使用{@link #keySet(Object)}当只关注键时,并且映射值(可能是短暂的)不使用或全部采用相同的映射值。
* <p>通过使用{@link java.util.concurrent.atomic.LongAdder}值和通过{@link #computeIfAbsent computeIfAbsent}进行初始化,ConcurrentHashMap可以作为可扩展的频率映射(一种直方图或多集)使用。例如,要将计数添加到{@code ConcurrentHashMap<String,LongAdder> freqs}中,可以使用{@code freqs.computeIfAbsent(k -> new LongAdder()).increment();}。** <p>此类及其视图和迭代器实现了{@link Map}和{@link Iterator}接口的所有<em>可选</em>方法。** <p>与{@link Hashtable}不同,但与{@link HashMap}不同,此类不允许将{@code null}用作键或值。** <p>ConcurrentHashMap支持一组顺序和并行的批量操作,与大多数{@link Stream}方法不同,这些操作被设计为即使在其他线程正在并发更新的映射上也可以安全地和通常地应用;例如,在计算共享注册表中的值的快照摘要时。有三种类型的操作,每种操作有四种形式,接受具有键、值、条目和(键、值)参数和/或返回值的函数。由于ConcurrentHashMap的元素没有以任何特定方式排序,并且可能在不同的并行执行中以不同的顺序处理,所以提供的函数的正确性不应依赖于任何排序,或者在计算正在进行时可能短暂更改的任何其他对象或值;除了forEach操作之外,理想情况下应该是无副作用的。{@link java.util.Map.Entry}对象上的批量操作不支持方法{@code setValue}。** <ul>* <li> forEach:对每个元素执行给定的操作。变体形式在执行操作之前对每个元素应用给定的转换。</li>
* <li> search: 返回应用于每个元素的给定函数的第一个可用的非空结果;当找到结果时,跳过进一步搜索。</li>** <li> reduce: 累积每个元素。提供的缩减函数不能依赖于排序(更正式地说,它应该是可结合和可交换的)。有五种变体:** <ul>** <li> 普通缩减。(对于(键,值)函数参数没有此方法的形式,因为没有相应的返回类型。)</li>** <li> 累积给定函数应用于每个元素的结果。</li>** <li> 缩减为标量双精度、长整型和整型,使用给定的基值。</li>** </ul>* </li>* </ul>** <p>这些批量操作接受一个{@code parallelismThreshold}参数。如果当前映射大小的估计值小于给定的阈值,则方法按顺序进行。使用{@code Long.MAX_VALUE}的值会抑制所有并行性。使用{@code 1}的值会通过将任务分割成足够的子任务来充分利用用于所有并行计算的{@link ForkJoinPool#commonPool()}。通常,您最初会选择这些极端值之一,然后测量使用中间值的性能,以权衡开销与吞吐量。
* <p>批量操作的并发属性遵循ConcurrentHashMap的属性:从{@code get(key)}和相关访问方法返回的任何非空结果都与相关的插入或更新之间存在happens-before关系。任何批量操作的结果都反映了这些每个元素关系的组合(但不一定是整个映射的原子操作,除非它以某种方式被认为是静止的)。相反,因为映射中的键和值永远不为null,所以null作为当前缺少任何结果的可靠原子指示器。为了保持这个属性,null作为所有非标量缩减操作的隐式基础。对于double、long和int版本,基础应该是一个与任何其他值组合返回该其他值的值(更正式地说,它应该是缩减的恒等元素)。大多数常见的缩减操作都具有这些属性;例如,使用基础0计算总和或使用基础MAX_VALUE计算最小值。** <p>作为参数提供的搜索和转换函数同样应该返回null以指示缺少任何结果(在这种情况下不使用)。在映射缩减的情况下,这还使得转换可以充当过滤器,如果不应该组合元素,则返回null(或者在原始专业化的情况下,返回身份基础)。在搜索或缩减操作中使用它们之前,您可以根据这个“null表示现在没有任何东西”的规则自己组合它们来创建复合转换和过滤。** <p>接受和/或返回Entry参数的方法维护键值关联。例如,在查找最大值的键时可能会有用。请注意,“普通”的Entry参数可以使用{@code new AbstractMap.SimpleEntry(k,v)}提供。
* <p>批量操作可能会突然完成,抛出在应用提供的函数中遇到的异常。在处理这些异常时,请注意其他同时执行的函数也可能抛出异常,或者如果第一个异常没有发生,它们也会抛出异常。** <p>与顺序形式相比,并行操作的加速是常见的,但不是保证的。如果用于并行计算的底层工作比计算本身更昂贵,那么在小型映射上对简短函数进行并行操作可能比顺序形式执行得更慢。同样,如果所有处理器都忙于执行不相关的任务,那么并行化可能不会导致实际的并行性。** <p>所有任务方法的所有参数都必须是非空的。** <p>此类是* <a href="{@docRoot}/../technotes/guides/collections/index.html">* Java集合框架</a>的成员。** @since 1.5* @author Doug Lea* @translator bo yao* @param <K> 此映射维护的键的类型* @param <V> 映射值的类型
*/
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
/**
* 概述:
*
* 这个哈希表的主要设计目标是在最小化更新争用的同时保持并发可读性(通常是get()方法,但也包括迭代器和相关方法)。次要目标是保持空间消耗与java.util.HashMap大致相同或更好,并支持多线程在空表上的高初始插入速率。
*
* 这个映射通常作为一个分桶(bucketed)哈希表。每个键值映射都保存在一个Node中。大多数节点都是基本Node类的实例,具有哈希、键、值和下一个字段。然而,还存在各种子类:TreeNodes以平衡树的形式排列,而不是列表。TreeBins保存TreeNodes集合的根节点。在调整大小期间,ForwardingNodes被放置在桶的头部。ReservationNodes在computeIfAbsent和相关方法中用作占位符。TreeBin、ForwardingNode和ReservationNode类型不保存普通用户键、值或哈希,并且在搜索等操作中很容易区分,因为它们具有负的哈希字段和空的键和值字段。(这些特殊节点要么不常见,要么是短暂的,因此携带一些未使用的字段的影响是微不足道的。)
*
* 表在第一次插入时被延迟初始化为2的幂大小。表中的每个桶通常包含一个Node列表(大多数情况下,列表只有零个或一个Node)。表访问需要volatile/atomic读取、写入和CAS操作。因为没有其他方法可以安排这个操作而不添加进一步的间接引用,所以我们使用内部操作(sun.misc.Unsafe)操作。
* 我们使用节点哈希字段的最高(符号)位进行控制
* 目的--它是可用的,因为寻址
* 约束。具有负哈希字段的节点在映射方法中被特殊处理或忽略。
*
* 在空箱中插入(通过put或其变体)的第一个节点是通过CAS直接将其设置为箱子。这是
* 在大多数键/哈希分布下,put操作中最常见的情况。其他更新操作(插入,
* 删除和替换)需要锁。我们不想浪费
* 与每个箱子关联的独立锁对象所需的空间,因此使用箱子列表本身的第一个节点作为
* 锁。这些锁的锁定支持依赖于内置的
* “synchronized”监视器。
*
* 但仅仅使用列表的第一个节点作为锁是不够的:当节点被锁定时,任何更新必须首先
* 验证它在锁定后是否仍然是第一个节点,如果不是,则重试。因为新节点总是附加到列表中,
* 一旦节点在一个箱子中首次出现,它就会一直保持第一个,直到被删除
* 或箱子变为无效(在调整大小时)。
*
* 每个箱子锁的主要缺点是,由同一个锁保护的箱子列表中的其他节点的其他更新
* 操作可能会停顿,例如当用户equals()或映射
* 函数花费很长时间时。然而,从统计上看,在
* 随机哈希码下,这不是一个常见的问题。理想情况下,
* 箱子中节点的频率遵循泊松分布
* (http://en.wikipedia.org/wiki/Poisson_distribution)的平均参数为0.5,
* 给定调整大小的阈值为0.75,尽管由于调整大小
* 粒度较大,方差较大。忽略方差,列表大小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: less than 1 in ten million
*
* 两个线程访问不同元素时的锁争用概率大约为1 / (8 * #elements),假设哈希值是随机的。
*
* 实际遇到的哈希码分布有时与均匀随机性显著偏离。这包括N > (1<<30)的情况,因此一些键必须发生碰撞。
* 同样,对于设计为具有相同哈希码或仅在掩码高位上有差异的多个键的愚蠢或敌对用法,我们使用一种次要策略,
* 该策略适用于一个bin中的节点数超过阈值的情况。这些TreeBin使用平衡树来保存节点(红黑树的一种特殊形式),
* 将搜索时间限制为O(log N)。TreeBin中的每个搜索步骤至少比常规列表慢两倍,但是考虑到N不能超过(1<<64)
* (在用完地址之前),这会将搜索步骤、锁持有时间等限制为合理的常数(最坏情况下每个操作检查大约100个节点),
* 只要键是可比较的(这是非常常见的——String、Long等)。TreeBin节点(TreeNodes)也保持与常规节点相同的
* “next”遍历指针,因此可以以相同的方式在迭代器中遍历。
* 当占用率超过百分比阈值时(通常为0.75,但见下文),表格将被调整大小。
* 任何注意到过满的桶的线程都可以在启动线程分配和设置替换数组后协助调整大小。
* 然而,这些其他线程不会停顿,而是可以继续进行插入等操作。
* 使用TreeBins可以在调整大小进行中免受过度填充的最坏情况影响。
* 调整大小通过将桶逐个从表格转移到下一个表格来进行。
* 然而,在这样做之前,线程会声明要传输的索引的小块(通过字段transferIndex),从而减少争用。
* sizeCtl字段中的生成标记确保调整大小不会重叠。
* 因为我们使用的是二次幂扩展,所以每个桶中的元素要么保持在相同的索引上,要么以二次幂的偏移量移动。
* 通过捕获旧节点的下一个字段不会更改的情况,我们消除了不必要的节点创建。
* 平均而言,当表格翻倍时,只有大约六分之一的节点需要克隆。
* 一旦这些节点不再被任何可能正在同时遍历表格的读取线程引用,它们将可以进行垃圾回收。
* 在传输时,旧表格桶只包含一个特殊的转发节点(哈希字段为"MOVED"),其中包含下一个表格作为其键。
* 在遇到转发节点时,访问和更新操作会重新启动,使用新表格。
* 每个bin的转移都需要其bin锁,这可能会在调整大小时等待锁定。
* 然而,由于其他线程可以加入并帮助调整大小而不是争夺锁定,
* 平均聚合等待时间随着调整大小的进行而变短。
* 转移操作还必须确保旧表和新表中的所有可访问的bin都可以被任何遍历使用。
* 这部分是通过从最后一个bin(table.length - 1)向上移动到第一个来安排的。
* 在看到转发节点时,遍历(参见Traverser类)安排移动到新表而不重新访问节点。
* 为了确保即使在无序移动时也不会跳过任何中间节点,
* 在遍历期间首次遇到转发节点时创建一个堆栈(参见TableStack类),
* 以在稍后处理当前表时保持其位置。这些保存/恢复机制的需求相对较少,
* 但是当遇到一个转发节点时,通常会遇到更多的转发节点。
* 因此,遍历器使用简单的缓存方案来避免创建太多新的TableStack节点。
* (感谢Peter Levart建议在这里使用堆栈。)
*
* 遍历方案还适用于对bin范围的部分遍历(通过备用的Traverser构造函数),
* 以支持分区的聚合操作。此外,只读操作如果转发到空表,则放弃,
* 这为关闭式清除提供了支持,但目前尚未实现。
*
* 惰性表初始化最小化了占用空间,直到第一次使用,
* 并且在第一个操作来自putAll、具有映射参数的构造函数或反序列化时也避免了调整大小。
* 这些情况尝试覆盖初始容量设置,但在竞争情况下无害地失败。
* 元素计数使用LongAdder的特殊化来维护。我们需要使用特殊化而不仅仅使用LongAdder,以便访问隐式的争用感知,从而导致创建多个CounterCells。计数机制避免了更新时的争用,但在并发访问期间如果读取太频繁可能会遇到缓存抖动。为了避免频繁读取,只有在向已经持有两个或更多节点的bin添加时才尝试在争用下进行调整。在均匀的哈希分布下,发生这种情况的概率在阈值处约为13%,这意味着只有约8个put检查阈值(并且在调整大小后,这个数量会更少)。
*
* TreeBins使用一种特殊的比较方式进行搜索和相关操作(这是我们不能使用现有集合(如TreeMaps)的主要原因)。TreeBins包含Comparable元素,但也可能包含其他元素,以及对于相同T而言不一定是Comparable的元素,因此我们不能在它们之间调用compareTo。为了处理这个问题,树首先按哈希值排序,然后按照Comparable.compareTo的顺序排序(如果适用)。在节点上查找时,如果元素不可比较或比较为0,则在哈希值相等的情况下可能需要搜索左右子节点。(这对应于如果所有元素都是非Comparable并且具有相同哈希值,则需要进行的完整列表搜索。)在插入时,为了保持总排序(或者在这里所需的尽可能接近),我们比较类和identityHashCode作为决胜者。红黑平衡代码是从pre-jdk-collections更新的(http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java),这又基于Cormen、Leiserson和Rivest的《算法导论》(CLR)。
* TreeBins还需要额外的锁机制。虽然在更新期间读者始终可以进行列表遍历,但不能进行树遍历,主要是因为可能会改变根节点和/或其链接的树旋转。TreeBins包括一个简单的读写锁机制,它寄生在主要的bin同步策略上:与插入或删除相关的结构调整已经被bin锁定(因此不能与其他写者冲突),但必须等待正在进行的读者完成。由于只能有一个这样的等待者,我们使用一个简单的方案,使用一个单独的“等待者”字段来阻塞写者。然而,读者永远不需要阻塞。如果持有根锁,则它们沿着慢速遍历路径(通过next指针)继续,直到锁可用或列表耗尽,以先到者为准。这些情况不快,但最大化了预期的总吞吐量。
*
* 为了保持与该类的先前版本的API和序列化兼容性,引入了几个奇怪之处。主要是:我们保留了但未使用的构造函数参数,引用并发级别。我们接受一个loadFactor构造函数参数,但只将其应用于初始表容量(这是我们唯一可以保证遵守它的时间)。我们还声明了一个未使用的“Segment”类,仅在序列化时以最小形式实例化。
*
* 此外,仅为了与该类的先前版本兼容,它扩展了AbstractMap,尽管它的所有方法都被覆盖,因此它只是无用的负担。
*/
/*---------------- 常量 --------------
*/
/**
* 最大可能的表容量。这个值必须是1<<30,以保持在Java数组分配和索引边界内,适用于2的幂次方表大小,并且进一步要求因为32位哈希字段的前两位用于控制目的。
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认的初始表容量。必须是2的幂次方(即至少为1)且最大不超过MAXIMUM_CAPACITY。
*/
private static final int DEFAULT_CAPACITY = 16;
/**
* 最大可能的(非2的幂)数组大小。
* toArray和相关方法所需。
*/
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 此表的默认并发级别。未使用,但为了与此类的先前版本兼容而定义。
*/
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* 此表的负载因子。在构造函数中对此值的覆盖仅影响初始表容量。
* 实际的浮点值通常不被使用 - 使用表达式{@code n - (n >>> 2)}来计算相关的调整阈值更简单。
*/
private static final float LOAD_FACTOR = 0.75f;
/**
* 使用树而不是列表的bin计数阈值。
* 当向具有至少这么多节点的bin添加元素时,bin将转换为树。
* 该值必须大于2,并且应至少为8,以便与关于在缩小时转换回普通bin的假设相匹配。
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 在调整大小操作期间,将(分割的)bin转换为非树形结构的bin计数阈值。
* 应该小于TREEIFY_THRESHOLD,并且最多为6,以便与删除时的收缩检测相匹配。
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 可以将箱子转化为树的最小表容量。
* (否则,如果一个箱子中的节点太多,表将被调整大小。)
* 该值应至少为4 * TREEIFY_THRESHOLD,以避免调整大小和树化阈值之间的冲突。
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 每个传输步骤的最小重新分配次数。范围被细分以允许多个调整大小线程。
* 此值作为下限,以避免调整大小器遇到过多的内存争用。
* 该值应至少为DEFAULT_CAPACITY。
*/
private static final int MIN_TRANSFER_STRIDE = 16;
/**
* 在sizeCtl中用于生成戳的位数。
* 对于32位数组,必须至少为6。
*/
private static int RESIZE_STAMP_BITS = 16;
/**
* 可以帮助调整大小的最大线程数。
* 必须适应32-RESIZE_STAMP_BITS位。
*/
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
/**
* 在sizeCtl中记录大小标记的位移。
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
/** Node哈希字段的编码。详见上文的解释。
*/
static final int MOVED = -1; // 转发节点的哈希值
static final int TREEBIN = -2; // 树根的哈希值
static final int RESERVED = -3; // 用于短暂预订的哈希
static final int HASH_BITS = 0x7fffffff; // 可用的普通节点哈希位
/** CPU的数量,用于限制一些大小的范围
*/
static final int NCPU = Runtime.getRuntime().availableProcessors();
/** 用于序列化兼容性。
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("segments", Segment[].class),
new ObjectStreamField("segmentMask", Integer.TYPE),
new ObjectStreamField("segmentShift", Integer.TYPE)
};
/*---------------- 节点 --------------
*/
/**
* 键值对条目。这个类不会作为一个可变的Map.Entry导出(即不支持setValue方法;参见下面的MapEntry),但可以用于只读遍历用于批量任务。具有负hash字段的Node的子类是特殊的,它们包含null键和值(但永远不会被导出)。否则,键和值永远不为null。
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return val; }
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final String toString(){ return key + "=" + val; }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* 为map.get()提供虚拟化支持;在子类中被重写。
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
/*---------------- 静态工具 --------------
*/
/**
* 将哈希的高位(XOR)扩展到低位,并强制将最高位设置为0。因为表使用的是2的幂掩码,所以只有在当前掩码上方的位不同的哈希集将始终发生碰撞。
* (已知的示例包括在小表中保存连续整数的Float键集。)因此,我们应用了一种将高位的影响向下传播的变换。
* 在速度、效用和位扩展的质量之间存在权衡。因为许多常见的哈希集已经合理分布(因此不受扩展的影响),并且因为我们使用树来处理bin中的大量碰撞集,
* 所以我们只需以最便宜的方式对一些移位的位进行XOR运算,以减少系统性损失,并将最高位的影响纳入索引计算中,否则由于表边界的限制,这些位将永远不会被使用。
*/
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
/**
* 返回给定所需容量的2的幂表大小。
* 参见Hackers Delight,第3.2节
*/
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
/**
* 如果x的形式为"class C implements Comparable<C>",则返回x的类,否则返回null。
*/
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
if ((c = x.getClass()) == String.class) // 绕过检查
return c;
if ((ts = c.getGenericInterfaces()) != null) {
for (int i = 0; i < ts.length; ++i) {
if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c) // 定义一个类型arg为c
return c;
}
}
}
return null;
}
/**
* 如果x与kc(k的筛选可比较类)匹配,则返回k.compareTo(x),否则返回0。
*/
@SuppressWarnings({"rawtypes","unchecked"}) // 用于将类型转换为Comparable。
static int compareComparables(Class<?> kc, Object k, Object x) {
return (x == null || x.getClass() != kc ? 0 :
((Comparable)k).compareTo(x));
}
/*---------------- 表元素访问 --------------
*/
/** Volatile访问方法用于表元素以及正在调整大小的下一个表的元素。所有对tab参数的使用都必须由调用者进行空检查。所有调用者还必须进行预检查,确保tab的长度不为零(或等效检查),从而确保任何采用哈希值与(长度-1)相与的索引参数都是有效的索引。请注意,为了正确处理用户的任意并发错误,这些检查必须在局部变量上操作,这解释了下面一些看起来奇怪的内联赋值。请注意,对setTabAt的调用总是在锁定区域内发生,因此原则上只需要释放排序,而不是完全的volatile语义,但目前编码为volatile写入以保守起见。
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
/*---------------- 字段 --------------
*/
/**
* 存储桶的数组。在第一次插入时进行延迟初始化。
* 大小始终是2的幂。可以直接通过迭代器访问。
*/
transient volatile Node<K,V>[] table;
/**
* 下一个要使用的表;仅在调整大小时非空。
*/
private transient volatile Node<K,V>[] nextTable;
/**
* 基本计数器值,主要在没有争用时使用,
* 但也作为表初始化竞争的后备方案。
* 通过CAS更新。
*/
private transient volatile long baseCount;
/**
* 表的初始化和调整大小控制。当为负数时,表示表正在初始化或调整大小:-1表示初始化,否则为-(1 + 活动调整大小线程的数量)。否则,当表为null时,表示在创建时要使用的初始表大小,或者为0表示使用默认值。初始化后,表示下一个元素计数值,用于调整表的大小。
*/
private transient volatile int sizeCtl;
/**
* 在调整大小时要拆分的下一个表索引(加一)。
*/
private transient volatile int transferIndex;
/**
* 在调整大小和/或创建CounterCells时使用的自旋锁(通过CAS锁定)。
*/
private transient volatile int cellsBusy;
/**
* 计数器单元的表格。当非空时,大小为2的幂。
*/
private transient volatile CounterCell[] counterCells;
// 视图
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
/*---------------- 公共操作 --------------
*/
/**
* 使用默认的初始表大小(16)创建一个新的空地图。
*/
public ConcurrentHashMap() {
}
/**
* 创建一个新的、空的映射表,初始表大小可容纳指定数量的元素,无需动态调整大小。
*
* @param initialCapacity 实现内部进行调整以容纳这么多元素。
* @throws IllegalArgumentException 如果元素的初始容量为负数
*/
public ConcurrentHashMap(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException();
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}
/**
* 创建一个与给定地图具有相同映射的新地图。
*
* @param m 地图
*/
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this.sizeCtl = DEFAULT_CAPACITY;
putAll(m);
}
/**
* 根据给定的元素数量(initialCapacity)和初始表密度(loadFactor)创建一个新的空映射表,
* 初始表大小基于给定的元素数量(initialCapacity)和初始表密度(loadFactor)。
*
* @param initialCapacity 初始容量。实现会根据指定的负载因子调整内部大小以容纳这么多元素。
* @param loadFactor 表密度(负载因子)用于确定初始表大小。
* @throws IllegalArgumentException 如果元素的初始容量为负数或负载因子为非正数。
*
* @since 1.6
*/
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, 1);
}
/**
* 使用给定的元素数量(initialCapacity)、表密度(loadFactor)和并发更新线程数量(concurrencyLevel)创建一个新的空映射,
* 并根据初始表大小进行内部调整。
*
* @param initialCapacity 初始容量。实现会根据指定的负载因子调整内部大小以容纳这么多元素。
* @param loadFactor 表密度(负载因子),用于确定初始表大小。
* @param concurrencyLevel 估计的并发更新线程数量。实现可能会将此值用作大小提示。
* @throws IllegalArgumentException 如果初始容量为负数,或者负载因子或并发级别为非正数。
*/
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (initialCapacity < concurrencyLevel) // 使用至少与箱子数量相同的箱子
initialCapacity = concurrencyLevel; // 作为估计的线程。
long size = (long)(1.0 + (long)initialCapacity / loadFactor);
int cap = (size >= (long)MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY : tableSizeFor((int)size);
this.sizeCtl = cap;
}
// 原始(自JDK1.2起)的Map方法
/**
* {@inheritDoc}
*/
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return sumCount() <= 0L; // 忽略瞬态负值
}
/**
* 返回指定键映射的值,
* 如果此映射不包含键的映射,则返回{@code null}。
*
* <p>更正式地说,如果此映射包含从键{@code k}到值{@code v}的映射,
* 使得{@code key.equals(k)},则此方法返回{@code v};否则返回{@code null}。
* (最多只能有一个这样的映射。)
*
* @throws NullPointerException 如果指定的键为null
*/
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
/**
* 测试指定的对象是否是此表中的键。
*
* @param key 可能的键
* @return {@code true} 如果指定的对象是此表中的键,根据{@code equals}方法确定;否则返回{@code false}
* @throws NullPointerException 如果指定的键为null
*/
public boolean containsKey(Object key) {
return get(key) != null;
}
/**
* 如果此映射将一个或多个键映射到指定值,则返回{@code true}。注意:此方法可能需要对映射进行完整遍历,比方法{@code containsKey}慢得多。
*
* @param value 要在此映射中测试其存在的值
* @return 如果此映射将一个或多个键映射到指定值,则返回{@code true}
* @throws NullPointerException 如果指定的值为null
*/
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
Node<K,V>[] t;
if ((t = table) != null) {
Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
for (Node<K,V> p; (p = it.advance()) != null; ) {
V v;
if ((v = p.val) == value || (v != null && value.equals(v)))
return true;
}
}
return false;
}
/**
* 将指定的键映射到此表中的指定值。
* 键和值都不能为null。
*
* <p>可以通过调用{@code get}方法并传入与原始键相等的键来检索值。
*
* @param key 要关联指定值的键
* @param value 要与指定键关联的值
* @return 与{@code key}关联的先前值,如果{@code key}没有映射,则返回{@code null}
* @throws NullPointerException 如果指定的键或值为null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
/** put和putIfAbsent的实现
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // 添加到空箱时不需要锁定
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
/**
* 将指定映射中的所有映射复制到此映射中。
* 这些映射将替换此映射对于指定映射中当前存在的任何键的任何映射。
*
* @param m 要存储在此映射中的映射
*/
public void putAll(Map<? extends K, ? extends V> m) {
tryPresize(m.size());
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
putVal(e.getKey(), e.getValue(), false);
}
/**
* 从该映射中删除键(及其对应的值)。
* 如果键不在映射中,则此方法不执行任何操作。
*
* @param key 需要被删除的键
* @return 与{@code key}关联的先前值,如果{@code key}没有映射,则返回{@code null}
* @throws NullPointerException 如果指定的键为null
*/
public V remove(Object key) {
return replaceNode(key, null, null);
}
/**
* 实现四个公共的remove/replace方法:
* 如果cv非空,则用v替换节点值,条件是匹配cv。如果结果值为null,则删除。
*/
final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
/**
* 从此映射中删除所有映射。
*/
public void clear() {
long delta = 0L; // 负数删除次数
int i = 0;
Node<K,V>[] tab = table;
while (tab != null && i < tab.length) {
int fh;
Node<K,V> f = tabAt(tab, i);
if (f == null)
++i;
else if ((fh = f.hash) == MOVED) {
tab = helpTransfer(tab, f);
i = 0; // 重新启动
}
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
Node<K,V> p = (fh >= 0 ? f :
(f instanceof TreeBin) ?
((TreeBin<K,V>)f).first : null);
while (p != null) {
--delta;
p = p.next;
}
setTabAt(tab, i++, null);
}
}
}
}
if (delta != 0L)
addCount(delta, -1);
}
/**
* 返回此映射中包含的键的{@link Set}视图。
* 该集合由映射支持,因此对映射的更改会反映在集合中,反之亦然。该集合支持元素的删除,
* 通过{@code Iterator.remove}、{@code Set.remove}、{@code removeAll}、
* {@code retainAll}和{@code clear}操作从映射中删除相应的映射。它不支持{@code add}或{@code addAll}操作。
*
* <p>该视图的迭代器和分割器是<a href="package-summary.html#Weakly"><i>弱一致的</i></a>。
*
* <p>该视图的{@code spliterator}报告{@link Spliterator#CONCURRENT}、{@link Spliterator#DISTINCT}和{@link Spliterator#NONNULL}。
*
* @return 视图集
*/
public KeySetView<K,V> keySet() {
KeySetView<K,V> ks;
return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
}
/**
* 返回此映射中包含的值的{@link Collection}视图。
* 该集合由映射支持,因此对映射的更改会反映在集合中,反之亦然。该集合支持元素删除,通过{@code Iterator.remove}、{@code Collection.remove}、{@code removeAll}、{@code retainAll}和{@code clear}操作从映射中删除相应的映射。它不支持{@code add}或{@code addAll}操作。
*
* <p>该视图的迭代器和分割器是<a href="package-summary.html#Weakly"><i>弱一致的</i></a>。
*
* <p>该视图的{@code spliterator}报告{@link Spliterator#CONCURRENT}和{@link Spliterator#NONNULL}。
*
* @return 集合视图
*/
public Collection<V> values() {
ValuesView<K,V> vs;
return (vs = values) != null ? vs : (values = new ValuesView<K,V>(this));
}
/**
* 返回此映射中包含的映射的{@link Set}视图。
* 该集合由映射支持,因此对映射的更改会反映在集合中,反之亦然。该集合支持元素的删除,
* 通过{@code Iterator.remove}、{@code Set.remove}、{@code removeAll}、
* {@code retainAll}和{@code clear}操作从映射中删除相应的映射。
*
* <p>该视图的迭代器和分割器是
* <a href="package-summary.html#Weakly"><i>弱一致性</i></a>的。
*
* <p>该视图的{@code spliterator}报告{@link Spliterator#CONCURRENT}、
* {@link Spliterator#DISTINCT}和{@link Spliterator#NONNULL}。
*
* @return 视图集合
*/
public Set<Map.Entry<K,V>> entrySet() {
EntrySetView<K,V> es;
return (es = entrySet) != null ? es : (entrySet = new EntrySetView<K,V>(this));
}
/**
* 返回此{@link Map}的哈希码值,即,对于映射中的每个键值对,
* {@code key.hashCode() ^ value.hashCode()}的总和。
*
* @return 此映射的哈希码值
*/
public int hashCode() {
int h = 0;
Node<K,V>[] t;
if ((t = table) != null) {
Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
for (Node<K,V> p; (p = it.advance()) != null; )
h += p.key.hashCode() ^ p.val.hashCode();
}
return h;
}
/**
* 返回此映射的字符串表示形式。字符串表示形式由一组键值映射(无特定顺序)组成,括在大括号(“{@code {}}”)中。相邻的映射由字符{@code ", "}(逗号和空格)分隔。每个键值映射由键后跟等号(“{@code =}”)后跟相关联的值呈现。
*
* @return 此映射的字符串表示形式
*/
public String toString() {
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
StringBuilder sb = new StringBuilder();
sb.append('{');
Node<K,V> p;
if ((p = it.advance()) != null) {
for (;;) {
K k = p.key;
V v = p.val;
sb.append(k == this ? "(this Map)" : k);
sb.append('=');
sb.append(v == this ? "(this Map)" : v);
if ((p = it.advance()) == null)
break;
sb.append(',').append(' ');
}
}
return sb.append('}').toString();
}
/**
* 将指定的对象与此映射进行比较以确定是否相等。
* 如果给定的对象是具有与此映射相同映射的映射,则返回{@code true}。
* 如果在执行此方法期间任一映射被并发修改,此操作可能返回误导性结果。
*
* @param o 要与此映射进行比较以确定是否相等的对象
* @return 如果指定的对象等于此映射,则返回{@code true}
*/
public boolean equals(Object o) {
if (o != this) {
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
for (Node<K,V> p; (p = it.advance()) != null; ) {
V val = p.val;
Object v = m.get(p.key);
if (v == null || (v != val && !v.equals(val)))
return false;
}
for (Map.Entry<?,?> e : m.entrySet()) {
Object mk, mv, v;
if ((mk = e.getKey()) == null ||
(mv = e.getValue()) == null ||
(v = get(mk)) == null ||
(mv != v && !mv.equals(v)))
return false;
}
}
return true;
}
/**
* 用于前一个版本的辅助类的简化版本,
* 为了保持序列化兼容性而声明
*/
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
/**
* 将{@code ConcurrentHashMap}实例的状态保存到流中(即序列化)。
* @param s 流
* @throws java.io.IOException 如果发生I/O错误
* @serialData
* 每个键值映射的键(Object)和值(Object),后跟一个空对。
* 键值映射以无特定顺序发出。
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 用于序列化兼容性
// 模拟从此类的先前版本中计算段的过程
int sshift = 0;
int ssize = 1;
while (ssize < DEFAULT_CONCURRENCY_LEVEL) {
++sshift;
ssize <<= 1;
}
int segmentShift = 32 - sshift;
int segmentMask = ssize - 1;
@SuppressWarnings("unchecked")
Segment<K,V>[] segments = (Segment<K,V>[])
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
for (int i = 0; i < segments.length; ++i)
segments[i] = new Segment<K,V>(LOAD_FACTOR);
s.putFields().put("segments", segments);
s.putFields().put("segmentShift", segmentShift);
s.putFields().put("segmentMask", segmentMask);
s.writeFields();
Node<K,V>[] t;
if ((t = table) != null) {
Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
for (Node<K,V> p; (p = it.advance()) != null; ) {
s.writeObject(p.key);
s.writeObject(p.val);
}
}
s.writeObject(null);
s.writeObject(null);
segments = null; // 丢弃
}
/**
* 从流中重构实例(即反序列化)。
* @param s 流
* @throws ClassNotFoundException 如果无法找到序列化对象的类
* @throws java.io.IOException 如果发生I/O错误
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
/** 为了提高典型情况下的性能,我们在读取时创建节点,然后在确定大小后放入表中。 * 然而,我们还必须验证唯一性并处理过度填充的容器,这需要专门版本的putVal机制。
*/
sizeCtl = -1; // 强制排除表结构
s.defaultReadObject();
long size = 0L;
Node<K,V> p = null;
for (;;) {
@SuppressWarnings("unchecked")
K k = (K) s.readObject();
@SuppressWarnings("unchecked")
V v = (V) s.readObject();
if (k != null && v != null) {
p = new Node<K,V>(spread(k.hashCode()), k, v, p);
++size;
}
else
break;
}
if (size == 0L)
sizeCtl = 0;
else {
int n;
if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
n = MAXIMUM_CAPACITY;
else {
int sz = (int)size;
n = tableSizeFor(sz + (sz >>> 1) + 1);
}
@SuppressWarnings("unchecked")
Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
int mask = n - 1;
long added = 0L;
while (p != null) {
boolean insertAtFront;
Node<K,V> next = p.next, first;
int h = p.hash, j = h & mask;
if ((first = tabAt(tab, j)) == null)
insertAtFront = true;
else {
K k = p.key;
if (first.hash < 0) {
TreeBin<K,V> t = (TreeBin<K,V>)first;
if (t.putTreeVal(h, k, p.val) == null)
++added;
insertAtFront = false;
}
else {
int binCount = 0;
insertAtFront = true;
Node<K,V> q; K qk;
for (q = first; q != null; q = q.next) {
if (q.hash == h &&
((qk = q.key) == k ||
(qk != null && k.equals(qk)))) {
insertAtFront = false;
break;
}
++binCount;
}
if (insertAtFront && binCount >= TREEIFY_THRESHOLD) {
insertAtFront = false;
++added;
p.next = first;
TreeNode<K,V> hd = null, tl = null;
for (q = p; q != null; q = q.next) {
TreeNode<K,V> t = new TreeNode<K,V>
(q.hash, q.key, q.val, null, null);
if ((t.prev = tl) == null)
hd = t;
else
tl.next = t;
tl = t;
}
setTabAt(tab, j, new TreeBin<K,V>(hd));
}
}
}
if (insertAtFront) {
++added;
p.next = first;
setTabAt(tab, j, p);
}
p = next;
}
table = tab;
sizeCtl = n - (n >>> 2);
baseCount = added;
}
}
// 并发映射方法
/**
* {@inheritDoc}
*
* @return 与指定键关联的先前值,如果键没有映射,则返回{@code null}
* @throws NullPointerException 如果指定的键或值为null
*/
public V putIfAbsent(K key, V value) {
return putVal(key, value, true);
}
/**
* {@inheritDoc}
*
* @throws NullPointerException 如果指定的键为null
*/
public boolean remove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
return value != null && replaceNode(key, null, value) != null;
}
/**
* {@inheritDoc}
*
* @throws NullPointerException 如果任何参数为null
*/
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
return replaceNode(key, newValue, oldValue) != null;
}
/**
* {@inheritDoc}
*
* @return 与指定键关联的先前值,如果键没有映射,则返回{@code null}
* @throws NullPointerException 如果指定的键或值为null
*/
public V replace(K key, V value) {
if (key == null || value == null)
throw new NullPointerException();
return replaceNode(key, value, null);
}
// JDK8+ Map扩展方法默认值的覆盖
/**
* 返回指定键映射的值,如果该映射不包含指定键的映射,则返回给定的默认值。
*
* @param key 要返回其关联值的键
* @param defaultValue 如果该映射不包含给定键的映射,则返回的值
* @return 键的映射(如果存在);否则返回默认值
* @throws NullPointerException 如果指定的键为null
*/
public V getOrDefault(Object key, V defaultValue) {
V v;
return (v = get(key)) == null ? defaultValue : v;
}
public void forEach(BiConsumer<? super K, ? super V> action) {
if (action == null) throw new NullPointerException();
Node<K,V>[] t;
if ((t = table) != null) {
Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
for (Node<K,V> p; (p = it.advance()) != null; ) {
action.accept(p.key, p.val);
}
}
}
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
if (function == null) throw new NullPointerException();
Node<K,V>[] t;
if ((t = table) != null) {
Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
for (Node<K,V> p; (p = it.advance()) != null; ) {
V oldValue = p.val;
for (K key = p.key;;) {
V newValue = function.apply(key, oldValue);
if (newValue == null)
throw new NullPointerException();
if (replaceNode(key, newValue, oldValue) != null ||
(oldValue = get(key)) == null)
break;
}
}
}
}
/**
* 如果指定的键尚未与值关联,
* 尝试使用给定的映射函数计算其值
* 并将其输入到此映射中,除非为null。整个
* 方法调用是原子性的,因此函数
* 每个键最多应用一次。其他线程对此映射的一些尝试更新操作
* 在计算过程中可能会被阻塞,因此计算应该简短而简单,
* 并且不能尝试更新此映射的任何其他映射。
*
* @param key 要关联指定值的键
* @param mappingFunction 计算值的函数
* @return 与指定键关联的当前(现有或计算)值,如果计算值为null,则返回null
* @throws NullPointerException 如果指定的键或mappingFunction为null
* @throws IllegalStateException 如果计算检测到
* 尝试递归更新此映射,否则永远无法完成
* @throws RuntimeException或Error 如果mappingFunction这样做,
* 在这种情况下,映射未建立
*/
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (key == null || mappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
Node<K,V> r = new ReservationNode<K,V>();
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
if ((val = mappingFunction.apply(key)) != null)
node = new Node<K,V>(h, key, val, null);
} finally {
setTabAt(tab, i, node);
}
}
}
if (binCount != 0)
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
boolean added = false;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek; V ev;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = e.val;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
if ((val = mappingFunction.apply(key)) != null) {
added = true;
pred.next = new Node<K,V>(h, key, val, null);
}
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null)
val = p.val;
else if ((val = mappingFunction.apply(key)) != null) {
added = true;
t.putTreeVal(h, key, val);
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (!added)
return val;
break;
}
}
}
if (val != null)
addCount(1L, binCount);
return val;
}
/**
* 如果指定键的值存在,则尝试使用键和其当前映射的值计算新的映射关系。
* 整个方法调用是原子性的。
* 在计算过程中,其他线程对该映射的一些尝试更新操作可能会被阻塞,
* 因此计算应该是简短且简单的,并且不能尝试更新该映射的任何其他映射。
*
* @param key 可能与值关联的键
* @param remappingFunction 计算值的函数
* @return 与指定键关联的新值,如果没有则返回null
* @throws NullPointerException 如果指定的键或remappingFunction为null
* @throws IllegalStateException 如果计算检测到对该映射的递归更新的尝试,
* 否则将永远无法完成
* @throws RuntimeException或Error 如果remappingFunction这样做,
* 在这种情况下映射不变
*/
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (key == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
break;
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(key, e.val);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(h, key, null)) != null) {
val = remappingFunction.apply(key, p.val);
if (val != null)
p.val = val;
else {
delta = -1;
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (binCount != 0)
break;
}
}
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
/**
* 尝试计算指定键及其当前映射值(如果没有当前映射值,则为{@code null})的映射。
* 整个方法调用是原子性的。
* 在计算过程中,其他线程对此映射的某些尝试更新操作可能会被阻塞,
* 因此计算应该简短而简单,并且不能尝试更新此Map的任何其他映射。
*
* @param key 要关联指定值的键
* @param remappingFunction 计算值的函数
* @return 与指定键关联的新值,如果没有则为null
* @throws NullPointerException 如果指定的键或remappingFunction为null
* @throws IllegalStateException 如果计算检测到对此映射的递归更新的尝试,
* 否则永远无法完成
* @throws RuntimeException或Error如果remappingFunction这样做,
* 在这种情况下映射不变
*/
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (key == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
Node<K,V> r = new ReservationNode<K,V>();
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
if ((val = remappingFunction.apply(key, null)) != null) {
delta = 1;
node = new Node<K,V>(h, key, val, null);
}
} finally {
setTabAt(tab, i, node);
}
}
}
if (binCount != 0)
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(key, e.val);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null) {
val = remappingFunction.apply(key, null);
if (val != null) {
delta = 1;
pred.next =
new Node<K,V>(h, key, val, null);
}
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 1;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null)
p = r.findTreeNode(h, key, null);
else
p = null;
V pv = (p == null) ? null : p.val;
val = remappingFunction.apply(key, pv);
if (val != null) {
if (p != null)
p.val = val;
else {
delta = 1;
t.putTreeVal(h, key, val);
}
}
else if (p != null) {
delta = -1;
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
/**
* 如果指定的键尚未与(非空)值关联,则将其与给定值关联起来。
* 否则,用给定的重新映射函数的结果替换该值,或者如果为null,则删除该值。整个方法调用是原子性的。
* 在计算过程中,其他线程对该映射的尝试更新操作可能会被阻塞,因此计算应该简短而简单,并且不能尝试更新该映射的任何其他映射。
*
* @param key 要关联指定值的键
* @param value 如果不存在,则使用的值
* @param remappingFunction 如果存在,则重新计算值的函数
* @return 与指定键关联的新值,如果没有则为null
* @throws NullPointerException 如果指定的键或remappingFunction为null
* @throws RuntimeException或Error如果remappingFunction这样做,则映射保持不变
*/
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (key == null || value == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
delta = 1;
val = value;
break;
}
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(e.val, value);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null) {
delta = 1;
val = value;
pred.next =
new Node<K,V>(h, key, val, null);
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 2;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r = t.root;
TreeNode<K,V> p = (r == null) ? null :
r.findTreeNode(h, key, null);
val = (p == null) ? value :
remappingFunction.apply(p.val, value);
if (val != null) {
if (p != null)
p.val = val;
else {
delta = 1;
t.putTreeVal(h, key, val);
}
}
else if (p != null) {
delta = -1;
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
// Hashtable 传统方法
/**
* 传统方法,测试某个键是否映射到指定的值。
* 该方法在功能上与{@link #containsValue(Object)}相同,仅用于确保与{@link java.util.Hashtable}类的完全兼容性,
* 在引入Java集合框架之前,Hashtable类支持此方法。
*
* @param value 要搜索的值
* @return {@code true} 当且仅当某个键映射到此表中的{@code value}参数,由{@code equals}方法确定;
* 否则返回{@code false}
* @throws NullPointerException 如果指定的值为null
*/
public boolean contains(Object value) {
return containsValue(value);
}
/**
* 返回此表中键的枚举。
*
* @return 此表中键的枚举
* @see #keySet()
*/
public Enumeration<K> keys() {
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
return new KeyIterator<K,V>(t, f, 0, f, this);
}
/**
* 返回此表中的值的枚举。
*
* @return 此表中的值的枚举
* @see #values()
*/
public Enumeration<V> elements() {
Node<K,V>[] t;
int f = (t = table) == null ? 0 : t.length;
return new ValueIterator<K,V>(t, f, 0, f, this);
}
// 仅适用于ConcurrentHashMap的方法
/**
* 返回映射的数量。应该使用此方法而不是{@link #size},因为ConcurrentHashMap可能包含超过int表示的映射。返回的值是一个估计值;如果存在并发插入或删除,则实际计数可能会有所不同。
*
* @return 映射的数量
* @since 1.8
*/
public long mappingCount() {
long n = sumCount();
return (n < 0L) ? 0L : n; // 忽略瞬态负值
}
/**
* 创建一个新的{@link Set},由给定类型到{@code Boolean.TRUE}的ConcurrentHashMap支持。
*
* @param <K> 返回的set的元素类型
* @return 新的set
* @since 1.8
*/
public static <K> KeySetView<K,Boolean> newKeySet() {
return new KeySetView<K,Boolean>
(new ConcurrentHashMap<K,Boolean>(), Boolean.TRUE);
}
/**
* 创建一个新的{@link Set},由给定类型到{@code Boolean.TRUE}的ConcurrentHashMap支持。
*
* @param initialCapacity 实现会进行内部调整以容纳这么多元素。
* @param <K> 返回集合的元素类型
* @return 新的集合
* @throws IllegalArgumentException 如果元素的初始容量为负数
* @since 1.8
*/
public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
return new KeySetView<K,Boolean>
(new ConcurrentHashMap<K,Boolean>(initialCapacity), Boolean.TRUE);
}
/**
* 返回此映射中键的{@link Set}视图,使用给定的公共映射值进行任何添加(即{@link
* Collection#add}和{@link Collection#addAll(Collection)})。
* 当然,只有在可以接受使用相同值来自此视图的所有添加时,才适用此方法。
*
* @param mappedValue 用于任何添加的映射值
* @return set视图
* @throws NullPointerException 如果mappedValue为null
*/
public KeySetView<K,V> keySet(V mappedValue) {
if (mappedValue == null)
throw new NullPointerException();
return new KeySetView<K,V>(this, mappedValue);
}
/*---------------- 特殊节点 --------------
*/
/**
* 在转移操作期间插入到bins头部的节点。
*/
static final class ForwardingNode<K,V> extends Node<K,V> {
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
Node<K,V> find(int h, Object k) {
// 循环以避免在转发节点上出现任意深度的递归
outer: for (Node<K,V>[] tab = nextTable;;) {
Node<K,V> e; int n;
if (k == null || tab == null || (n = tab.length) == 0 ||
(e = tabAt(tab, (n - 1) & h)) == null)
return null;
for (;;) {
int eh; K ek;
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
if (eh < 0) {
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
continue outer;
}
else
return e.find(h, k);
}
if ((e = e.next) == null)
return null;
}
}
}
}
/**
* 在computeIfAbsent和compute中使用的占位符节点
*/
static final class ReservationNode<K,V> extends Node<K,V> {
ReservationNode() {
super(RESERVED, null, null, null);
}
Node<K,V> find(int h, Object k) {
return null;
}
}
/*---------------- 表格初始化和调整大小 --------------
*/
/**
* 返回调整大小为n的表的戳记位。
* 在左移RESIZE_STAMP_SHIFT时必须为负数。
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
/**
* 初始化表格,使用sizeCtl中记录的大小。
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // 丢失初始化竞争;只是旋转
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
/**
* 增加计数,如果表太小且尚未调整大小,则启动转移。如果已经调整大小,则在有工作可用时帮助执行转移。在转移后重新检查占用情况,以查看是否已经需要另一个调整大小,因为调整大小滞后于添加操作。
*
* @param x 要添加的计数
* @param check 如果<0,则不检查调整大小;如果<= 1,则仅检查是否无竞争
*/
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
s = sumCount();
}
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
/**
* 帮助判断是否正在进行调整大小。
*/
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
int rs = resizeStamp(tab.length);
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
/**
* 尝试调整表的大小以容纳给定数量的元素。
*
* @param size 元素的数量(不需要完全准确)
*/
private final void tryPresize(int size) {
int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
tableSizeFor(size + (size >>> 1) + 1);
int sc;
while ((sc = sizeCtl) >= 0) {
Node<K,V>[] tab = table; int n;
if (tab == null || (n = tab.length) == 0) {
n = (sc > c) ? sc : c;
if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if (table == tab) {
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
}
}
else if (c <= sc || n >= MAXIMUM_CAPACITY)
break;
else if (tab == table) {
int rs = resizeStamp(n);
if (sc < 0) {
Node<K,V>[] nt;
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
}
}
}
/**
* 将每个bin中的节点移动和/或复制到新表中。详见上文说明。
*/
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // 分割范围
if (nextTab == null) { // 初始化
try {
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // 尝试处理 OOME(Out Of Memory Error)
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
transferIndex = n;
}
int nextn = nextTab.length;
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
boolean advance = true;
boolean finishing = false; // 确保在提交nextTab之前进行清除
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
while (advance) {
int nextIndex, nextBound;
if (--i >= bound || finishing)
advance = false;
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
if (finishing) {
nextTable = null;
table = nextTab;
sizeCtl = (n << 1) - (n >>> 1);
return;
}
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
i = n; // 在提交之前重新检查
}
}
else if ((f = tabAt(tab, i)) == null)
advance = casTabAt(tab, i, null, fwd);
else if ((fh = f.hash) == MOVED)
advance = true; // 已经处理过
else {
synchronized (f) {
if (tabAt(tab, i) == f) {
Node<K,V> ln, hn;
if (fh >= 0) {
int runBit = fh & n;
Node<K,V> lastRun = f;
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
if (runBit == 0) {
ln = lastRun;
hn = null;
}
else {
hn = lastRun;
ln = null;
}
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof TreeBin) {
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
++lc;
}
else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
/*---------------- 计数器支持 --------------
*/
/**
* 用于分发计数的填充单元。改编自LongAdder和Striped64。有关说明,请参见它们的内部文档。
*/
@sun.misc.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
// 查看LongAdder版本以获取说明
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // 强制初始化
h = ThreadLocalRandom.getProbe();
wasUncontended = true;
}
boolean collide = false; // 如果最后一个槽非空,则为True
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
if ((as = counterCells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // 尝试附加新的单元格
CounterCell r = new CounterCell(x); // 乐观创建
if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean created = false;
try { // 重新检查锁定
CounterCell[] rs; int m, j;
if ((rs = counterCells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
created = true;
}
} finally {
cellsBusy = 0;
}
if (created)
break;
continue; // 插槽现在非空
}
}
collide = false;
}
else if (!wasUncontended) // CAS已知会失败
wasUncontended = true; // 继续重新哈希后
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
else if (counterCells != as || n >= NCPU)
collide = false; // 最大尺寸或过期
else if (!collide)
collide = true;
else if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
if (counterCells == as) {// 扩展表格,除非过时
CounterCell[] rs = new CounterCell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
counterCells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // 重试,使用扩展表格
}
h = ThreadLocalRandom.advanceProbe(h);
}
else if (cellsBusy == 0 && counterCells == as &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // 初始化表格
if (counterCells == as) {
CounterCell[] rs = new CounterCell[2];
rs[h & 1] = new CounterCell(x);
counterCells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // 退回使用基础
}
}
/*---------------- 从/到 TreeBins 的转换 --------------
*/
/**
* 除非表太小,否则替换给定索引处bin中的所有链接节点,否则进行调整大小。
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
/**
* 返回一个替换给定列表中的非TreeNode的列表。
*/
static <K,V> Node<K,V> untreeify(Node<K,V> b) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = b; q != null; q = q.next) {
Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}
/*---------------- 树节点 --------------
*/
/**
* 用于TreeBins中的节点
*/
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // 红黑树链接
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // 需要在删除时取消链接下一个节点
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next,
TreeNode<K,V> parent) {
super(hash, key, val, next);
this.parent = parent;
}
Node<K,V> find(int h, Object k) {
return findTreeNode(h, k, null);
}
/**
* 返回给定根节点开始的给定键的TreeNode(如果找不到则返回null)。
*/
final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
if (k != null) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk; TreeNode<K,V> q;
TreeNode<K,V> pl = p.left, pr = p.right;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.findTreeNode(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
}
return null;
}
}
/*---------------- 树形垃圾箱 --------------
*/
/**
* 用于链表头部的TreeNodes。TreeBins不保存用户的键或值,而是指向TreeNodes列表和它们的根节点。它们还维护了一个寄生的读写锁,强制写入者(持有bin锁)在树重构操作之前等待读取者(不持有锁)完成。
*/
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;
volatile TreeNode<K,V> first;
volatile Thread waiter;
volatile int lockState;
// 锁状态的值
static final int WRITER = 1; // 在持有写锁的情况下设置
static final int WAITER = 2; // 设置等待写锁时
static final int READER = 4; // 增加设置读锁的值
/**
* 在哈希码相等且不可比较时,用于排序插入的决策工具。
* 我们不需要一个完全的顺序,只需要一个一致的插入规则来保持重新平衡时的等价性。
* 比必要更进一步的决策简化了测试。
*/
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0)
d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1);
return d;
}
/**
* 创建以b为首的初始节点集的bin。
*/
TreeBin(TreeNode<K,V> b) {
super(TREEBIN, null, null, null);
this.first = b;
TreeNode<K,V> r = null;
for (TreeNode<K,V> x = b, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
if (r == null) {
x.parent = null;
x.red = false;
r = x;
}
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
for (TreeNode<K,V> p = r;;) {
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
r = balanceInsertion(r, x);
break;
}
}
}
}
this.root = r;
assert checkInvariants(root);
}
/**
* 获取用于树重构的写锁。
*/
private final void lockRoot() {
if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
contendedLock(); // 将代码分离到单独的方法中
}
/**
* 释放用于树重构的写锁。
*/
private final void unlockRoot() {
lockState = 0;
}
/**
* 可能会阻塞等待根锁。
*/
private final void contendedLock() {
boolean waiting = false;
for (int s;;) {
if (((s = lockState) & ~WAITER) == 0) {
if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
if (waiting)
waiter = null;
return;
}
}
else if ((s & WAITER) == 0) {
if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
waiting = true;
waiter = Thread.currentThread();
}
}
else if (waiting)
LockSupport.park(this);
}
}
/**
* 返回匹配的节点或者null(如果没有)。尝试使用从根节点开始的树比较进行搜索,但是当锁不可用时继续进行线性搜索。
*/
final Node<K,V> find(int h, Object k) {
if (k != null) {
for (Node<K,V> e = first; e != null; ) {
int s; K ek;
if (((s = lockState) & (WAITER|WRITER)) != 0) {
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
e = e.next;
}
else if (U.compareAndSwapInt(this, LOCKSTATE, s,
s + READER)) {
TreeNode<K,V> r, p;
try {
p = ((r = root) == null ? null :
r.findTreeNode(h, k, null));
} finally {
Thread w;
if (U.getAndAddInt(this, LOCKSTATE, -READER) ==
(READER|WAITER) && (w = waiter) != null)
LockSupport.unpark(w);
}
return p;
}
}
}
return null;
}
/**
* 查找或添加一个节点。
* @return 如果添加了则返回null
*/
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
for (TreeNode<K,V> p = root;;) {
int dir, ph; K pk;
if (p == null) {
first = root = new TreeNode<K,V>(h, k, v, null, null);
break;
}
else if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.findTreeNode(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.findTreeNode(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
TreeNode<K,V> x, f = first;
first = x = new TreeNode<K,V>(h, k, v, f, xp);
if (f != null)
f.prev = x;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
if (!xp.red)
x.red = true;
else {
lockRoot();
try {
root = balanceInsertion(root, x);
} finally {
unlockRoot();
}
}
break;
}
}
assert checkInvariants(root);
return null;
}
// 之后代码删除,字数超限制
}