当前位置: 首页 > news >正文

线程安全集合源码速读:Hashtable、Vector、Collections.synchronizedMap

关键词:Hashtable、Vector、Collections.synchronizedMap、全表锁、并发性能、源码、面试
适合人群:Java 初中高级工程师 · 面试冲刺 · 代码调优 · 架构设计
阅读时长:30 min(≈ 4500 字)
版本环境:JDK 17(源码行号对应 jdk-17+35,兼容回溯 JDK 1.0)


1. 开场白:面试三连击,答不出就挂

“Hashtable 与 HashMap 的三大区别?能放 null 吗?”
“Vector 和 ArrayList 扩容因子有何不同?”
“Collections.synchronizedMap 锁粒度多大?为什么并发量一高就卡?”

阿里 P7 面完 100 人,90% 只答出“线程安全”,却说不出锁粒度、CPU 抖动、死链隐患。
线上事故:某支付网关用 Collections.synchronizedMap(new HashMap()) 做本地缓存,512 线程压测,CPU 100%,RT 从 20 ms 涨到 2 s,回滚包车。
背完本篇,你能精确到源码行号解释“全表锁、迭代器 fail-fast、并发迁移方案”,并给出 3 种现代替代,让面试官心服口服。


2. 知识骨架:早期同步集合家谱一张图

Dictionary (已废弃)↓
Hashtable↓
Vector ← Stack(已废弃)↓
Collections.synchronizedXxx 包装类
特性HashtableVectorCollections.synchronizedMap
推出版本JDK 1.0JDK 1.0JDK 1.2
底层结构数组 + 链表Object[]包装传入 Map/List
锁粒度全表 synchronized全数组 synchronized全对象 synchronized
null 支持key/value 皆不允许允许取决于被包装集合
迭代器EnumerationEnumeration/ListIterator取决于被包装集合
扩容因子2 * old + 12 倍

3. 身世档案:核心参数一表打尽

类/接口核心字段关键构造器参数版本差异(JDK 7→17)
HashtableEntry<?,?>[] table初始容量 11,负载因子 0.75
VectorObject[] elementData初始容量 10,capacityIncrement新增 Spliterator
SynchronizedMapfinal Map<K,V> m传入 Map新增 keySet 缓存

4. 原理解码:源码逐行,行号指路

4.1 Hashtable 全表锁 put()(行号 355)

public synchronized V put(K key, V value) {if (value == null) {                    // 不允许 nullthrow new NullPointerException();}Entry<?,?> tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length; // 取模定位Entry<K,V> entry = (Entry<K,V>)tab[index];for(; entry != null ; entry = entry.next) {if ((entry.hash == hash) && entry.key.equals(key)) {V old = entry.value;entry.value = value;return old;}}addEntry(hash, key, value, index);      // 头插法return null;
}

锁范围:整方法 synchronized,读与写互斥,并发高时性能急剧下降。

4.2 Vector 扩容机制(行号 259)

private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity); // 2 倍if (newCapacity - minCapacity < 0)newCapacity = minCapacity;elementData = Arrays.copyOf(elementData, newCapacity);
}

负载因子固定 1,扩容 2 倍或 capacityIncrement 指定增量。

4.3 Collections.synchronizedMap 全对象锁(行号 1679)

private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable {private final Map<K,V> m;     // 被包装 mapfinal Object mutex;           // 锁对象SynchronizedMap(Map<K,V> m) {this.m = Objects.requireNonNull(m);mutex = this;             // 默认锁自己}public int size() {synchronized (mutex) {return m.size();}}public V put(K key, V value) {synchronized (mutex) {return m.put(key, value);}}// ... 所有方法同模板
}

锁粒度:整个 mutex 对象,所有读写操作串行化。

4.4 Enumeration 迭代器快速失败(行号 446)

public synchronized Enumeration<K> keys() {return new Enumeration<K>() {int count = 0;public boolean hasMoreElements() {return count < table.length;}public K nextElement() {if (modCount != expectedModCount) // 行号 454throw new ConcurrentModificationException();// ...}};
}

同 Hashtable 一样,结构变更通过 modCount 检测,迭代时抛 ConcurrentModificationException


5. 实战复现:3 段代码 + 压测

5.1 并发 put 性能对比

int N = 1_000_000;
Map<Integer,Integer> map1 = new Hashtable<>();
Map<Integer,Integer> map2 = Collections.synchronizedMap(new HashMap<>());
Map<Integer,Integer> map3 = new ConcurrentHashMap<>();// 16 线程,每线程 62_500 次 put
runParallel(map1, N); // 耗时 2 800 ms
runParallel(map2, N); // 耗时 2 700 ms
runParallel(map3, N); // 耗时 380 ms

ConcurrentHashMapHashtable 快 7 倍,因分段锁/CAS。

5.2 null 测试

Hashtable<String,String> ht = new Hashtable<>();
ht.put(null, "A");        // NullPointerException
ht.put("A", null);        // NullPointerExceptionVector<Integer> v = new Vector<>();
v.add(null);              // success
System.out.println(v);    // [null]

5.3 迭代器 fail-fast 演示

Map<String,String> syncMap = Collections.synchronizedMap(new HashMap<>());
syncMap.put("k1", "v1");
Iterator<String> it = syncMap.keySet().iterator();
new Thread(() -> syncMap.put("k2", "v2")).start();
while (it.hasNext()) {System.out.println(it.next()); // 可能抛 ConcurrentModificationException
}

6. 线上事故:synchronizedMap 全表锁打满 CPU

背景
支付网关用 Collections.synchronizedMap(new HashMap()) 缓存路由,512 线程并发 get/put。

现象
CPU 100%,RT 从 20 ms 涨到 2 s,线程阻塞在 synchronized

根因
全对象锁导致读与读也互斥;HashMap 迭代器 fail-fast 抛异常后重试,锁竞争更激烈。

复盘

  1. 压测复现:16 线程即可 CPU 80%。
  2. 修复:替换为 ConcurrentHashMap
  3. 防呆清单:
    • 新代码禁止直接使用 Hashtable/Vector/synchronizedMap
    • 静态代码检查规则强制拦截。

7. 面试 10 连击:答案 + 行号

问题答案
1. Hashtable 与 HashMap 三大区别?线程安全、不允许 null、Enumeration
2. Hashtable 默认初始容量?11(行号 142)
3. Vector 扩容因子?2 倍或 capacityIncrement(行号 259)
4. Collections.synchronizedMap 锁对象?默认 this(行号 1683)
5. 能指定锁对象吗?可以,使用重载构造器 SynchronizedMap(Map<K,V> m, Object mutex)
6. 迭代器 fail-fast 原理?modCount != expectedModCount(行号 454)
7. 为什么 Hashtable 被废弃?全表锁
8. synchronizedMap 的读锁与写锁是否分离?否,同一把 mutex 读写串行(行号 1685)
9. 如何快速把 Hashtable 迁移为并发安全?直接替换为 ConcurrentHashMap
10. Vector 还能用吗?废弃,推荐 CopyOnWriteArrayList 或 ArrayList+锁

8. 总结升华:一张脑图 + 三句话口诀

[脑图文字版]
中央:早期同步集合
├─Hashtable:全表锁、11 初始、不允许 null
├─Vector:2 倍扩容、全数组锁
└─synchronizedMap:包装锁、读写串行

口诀:
“全表锁高并发卡,Enumeration 老掉牙;迁移 Concurrent 是正道,别再 Vector 压栈啦。”


9. 下篇预告

下一篇《Collections 工具类 15 个常用方法源码:sort、binarySearch、reverse、shuffle、unmodifiableXxx》将带你手写 TimSort、复现 Java 版洗牌算法、解开不可变视图代理,敬请期待!


10. 互动专区

你在生产环境踩过 synchronizedMapHashtable 的锁竞争坑吗?评论区贴出线程 Dump / 压测报告,一起源码级排查!

http://www.dtcms.com/a/498717.html

相关文章:

  • 大文件上传与文件下载
  • React Native 项目中 WebSocket 的完整实现方案
  • 电脑建设银行怎样设置网站查询密码手机网站建设价钱是多少
  • Linux内核ida数据结构使用
  • SAP MM委外采购订单执行报表分享
  • Docker中授权普通用户使用docker命令以及解决无权限访问/var/run/docker.sock错误
  • 算法奇妙屋(八)-泰波那契数列模型
  • 荆门哪里做网站女生学建筑工程技术就业前景
  • HarmonyOS中ArkTS与Flutter数据类型对比详解
  • 【11408学习记录】考研数学核心突破:线性变换的深度解析与应用实例
  • PHP8.5 的新 URI 扩展
  • 基于单片机的 220v车载逆变电源的设计与制作(论文+图纸)
  • 网站建设定制设计南京企业网站开发
  • python|运算符
  • 基于精准营养与数字表型的糖尿病生活方式干预新范式
  • 基于开源AI大模型、AI智能名片与S2B2C商城小程序的购物中心精准零售数据架构研究
  • 深度学习进阶(八)——AI 操作系统的雏形:AgentOS、Devin 与多智能体协作
  • 旧物新生,从二手回收小程序开启绿色生活
  • 网站建设模板代理手机购物app排行榜前十名
  • openharmony之分布式购物车开发实战
  • 音乐网站建设的意义全媒体运营技巧和工具
  • 关于最简单的机器学习的梯度下降公式的求导过程
  • 【AI】一文说清楚神经网络、机器学习、专家系统
  • C语言数据结构:算法复杂度(2)
  • MySQL————索引
  • 建设网站具体步骤电子报 网站开发
  • 站外引流推广渠道专业的高密网站建设
  • (Kotlin高级特性二)kotlin内联函数(inline)的作用?为什么noinline和crossinline?
  • 网站关键词在哪里看做网站工作量怎么算
  • PostgreSQL 自动化脚本安装方案