黑色星期五之夜起风了孤勇者stay模组
466.00M · 2025-10-19
null
键/值?约束(Constraints)
成本模型(Cost Model)
最小原语(Primitives)
可验证结论(Check)
Collections.synchronizedMap(new HashMap<>())
的吞吐差异。null
,迭代 fail-fast;并发不安全。null
;在高并发下优于全局锁方案。Node<K,V>[] table
,桶(bin)冲突时使用 链表,当桶内结点数超过 树化阈值(默认 8) 且容量达到 最小树化容量(默认 64) 时转为 红黑树;当树结点数低于 反树化阈值(默认 6) 时退回链表。(hash = key.hashCode()) ^ (hash >>> 16)
,减少高位信息丢失导致的桶集中。0.75
,在空间与查找冲突率之间折中;阈值 threshold = capacity * loadFactor
触发扩容。null
键与值(null
键固定落在桶 0)。remove
)会抛 ConcurrentModificationException
。equals/hashCode
契约:不符合将导致查找失败或桶分布异常。equals/hashCode
的字段发生改变,会导致“找不回去”。CAS
初始化 table
;桶为空时使用 CAS
放置首节点;桶非空时对桶头节点(或 TreeBin
)进行 synchronized
桶级锁 以串行化更新。compute/computeIfAbsent/merge
)在桶级锁保护下执行,避免 ABA 与不变量破坏。sizeCtl
控制并发度;出现 ForwardingNode
表示该桶正在迁移,其他线程会 help transfer。ConcurrentModificationException
。null
键/值:避免歧义(无法区分“键不存在”与“键存在但值为 null”),也是并发计算方法的前提。size()
可能是近似值(并发下),JDK 8+ 进行了分布式计数;若需要准确性,遍历或使用 mappingCount()
(JDK 8 没有该方法,JDK 11+ 提供 mappingCount()
返回 long)。computeIfAbsent
可能成为热点;可引入 分区(striping) 或 分层缓存。forEach
等批处理 API 在大表上可能与迁移竞争,注意任务切分与批量大小。add/remove/set
等写操作会在 独占锁(ReentrantLock) 下复制出 新数组,修改后 原子地替换 引用。CopyOnWriteArrayList
之外的结构(如 ConcurrentHashMap
+ 有序视图,或 ImmutableList
+ 引用切换)。import java.util.*;
public class HashMapCollisionDemo {
static class BadKey {
final int id;
BadKey(int id) { this.id = id; }
@Override public int hashCode() { return 42; } // 故意制造高碰撞
@Override public boolean equals(Object o) {
return (o instanceof BadKey) && ((BadKey) o).id == id;
}
}
public static void main(String[] args) {
Map<BadKey, Integer> m = new HashMap<>();
for (int i = 0; i < 100; i++) m.put(new BadKey(i), i);
System.out.println("size=" + m.size());
// 可在调试器中观察桶长度变化与是否树化(需要自行打印内部结构或借助 JFR/可视化)
}
}
import java.util.*;
import java.util.concurrent.*;
public class MapThroughputCompare {
static final int THREADS = 32, OPS = 200_000;
public static void main(String[] args) throws Exception {
compare(new ConcurrentHashMap<>());
compare(Collections.synchronizedMap(new HashMap<>()));
}
static void compare(Map<Integer, Integer> map) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(THREADS);
long t0 = System.nanoTime();
for (int i = 0; i < THREADS; i++) {
es.submit(() -> {
ThreadLocalRandom rnd = ThreadLocalRandom.current();
for (int j = 0; j < OPS; j++) {
int k = rnd.nextInt(100_000);
map.put(k, k);
map.get(k);
}
});
}
es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES);
long t1 = System.nanoTime();
System.out.printf("%s took %.2f ms, size=%d%n", map.getClass().getSimpleName(), (t1 - t0) / 1e6, map.size());
}
}
import java.util.*;
import java.util.concurrent.*;
public class CowalDemo {
public static void main(String[] args) throws Exception {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) list.add(i);
// 迭代快照:下面的写入对本次迭代不可见
for (Integer v : list) {
if (v == 5) list.add(999); // 写时复制,迭代仍遍历旧数组,不抛异常
System.out.print(v + " ");
}
System.out.println("nlist size after add=" + list.size());
}
}
CopyOnWriteArrayList
(列表场景)或 ConcurrentHashMap
+ 定期生成不可变快照(Map 场景)。ConcurrentHashMap
,并根据热点键/值进行 分区(striping)、分层缓存 或 批量操作。LinkedHashMap
(单线程)或 ConcurrentSkipListMap
(并发,按键有序)。List<WeakReference<T>>
或分段结构)降低复制成本。null
?
computeIfAbsent
等 API 将出现歧义。null
。THREADS/OPS
,写结论。add/remove
写入,记录 GC 日志与暂停,评估是否满足你的 SLO;给出替代方案。equals/hashCode
写单元测试;避免可变键。466.00M · 2025-10-19
41.05M · 2025-10-19
96.35MB · 2025-10-19