synchronized在分布式环境下根本不可用,因其依赖JVM内部monitor和对象头Mark Word,仅在单个进程内存中生效,无法跨机器、跨JVM协同,不具备分布式锁所需的互斥性、防死锁和容错性。

synchronized 依赖 JVM 内部 monitor 和对象头 Mark Word,天生只在单个进程内存中生效。它无法跨机器、跨 JVM 协同,所以在分布式锁场景下不具备可扩展性——不是“不够好”,而是根本不可用。
对象锁的运行边界非常明确
Java 对象锁(包括 synchronized 和 ReentrantLock)的锁定动作全部发生在当前 JVM 的堆内存与线程栈中:
- 锁状态记录在对象头,仅本进程可见;
- monitor 是 JVM 级组件,不同实例的 JVM 各自维护一套,彼此无通信;
- 没有跨网络协调能力,也无法感知其他节点是否已加锁。
哪怕把同一段加锁代码部署到 100 台服务器上,每台机器上的锁都只约束自己那台的线程,对其他机器完全透明。这就像 100 个互不相识的人各自在自家门口挂了一把锁,却以为锁住了整条街。
分布式环境下对象锁失效的典型表现
当业务从单体迁移到集群,以下问题会立刻暴露:
立即学习“Java免费学习笔记(深入)”;
- 超卖:多个实例同时读取库存为 1,各自扣减后库存变为 0、-1、-2……
- 重复执行:定时任务在每个节点都触发,导致发多条短信、生成多份报表;
- 缓存击穿加剧:多个节点同时发现缓存缺失,一拥而上重建热点数据,压垮数据库;
- 数据覆盖:两个节点读取同一配置,各自修改后写回,后写者覆盖前者结果。
真正可扩展的替代方案需外部协调
要支持横向扩容、多实例协同,锁必须脱离 JVM,交由所有节点共同信任的第三方服务管理:
- Redis 方案:利用 SETNX + EXPIRE 原子指令(或 Lua 脚本),配合唯一 client ID 和自动续期,兼顾性能与可靠性;
- ZooKeeper 方案:基于临时顺序节点和 Watcher 机制,天然支持公平排队与自动释放,适合强一致性要求场景;
- 数据库方案:借助唯一索引或 SELECT FOR UPDATE,简单但吞吐受限,适用于低频、非核心路径。
这些方案的核心共性是:锁的状态存储在共享存储中,获取与释放操作具备跨节点可见性,且自带过期机制防止死锁。
扩展性不是优化问题,而是架构选择问题
试图“增强”对象锁去适配分布式环境(比如加代理、改字节码、套中间件)通常得不偿失。它违背了 synchronized 的设计初衷,也掩盖了分布式系统本质的协调需求。正确的做法是承认单机锁与分布式锁属于不同抽象层级——前者管线程,后者管线程+进程+网络——并在架构演进早期就引入合适的分布式协调服务。


















