synchronized在分布式环境下完全失效,因其仅作用于单个jvm,各实例无共享内存与统一监视器,无法实现跨进程互斥,导致超卖等问题;分布式锁需满足互斥性、防死锁、容错性三大条件。

synchronized在分布式环境下完全失效,根本原因在于它只作用于单个JVM内部——不同服务器上的多个Java进程互不共享内存、不共用对象监视器,自然无法感知彼此的锁状态。哪怕三台机器上都运行着一模一样的同步代码,它们各自加的锁也只管自己那台机器里的线程,对其他实例毫无约束力。超卖、重复提交、数据覆盖等问题,正是这种“各锁各的”导致的。
为什么synchronized跨不了机器
synchronized底层依赖对象头中的Mark Word和JVM内置的monitor机制,所有锁操作都在本进程内存中完成。集群中每个服务实例都是独立JVM,没有共享堆、没有跨进程通信能力,也就不存在“统一锁管理者”。它不是不好用,而是设计之初就没打算解决分布式问题——它是单机并发的方案,不是集群协同的方案。
分布式锁必须满足的三个硬性条件
一个真正可用的分布式锁不能只是“看起来像锁”,必须同时做到:
- 互斥性:任意时刻,全局只有一个客户端能成功持有锁;
- 防死锁:持有锁的节点宕机或业务异常中断,锁必须自动过期释放,不能永久占用;
- 容错性:锁服务本身支持高可用,比如Redis主从切换、ZooKeeper集群多数派写入,不能因单点故障导致整个系统锁不可用。
主流分布式锁方案对比与演进脉络
从简单到可靠,分布式锁经历了几轮典型实践:
- 数据库行锁(SELECT ... FOR UPDATE):利用事务隔离级别实现互斥,实现最轻,但吞吐低、易成数据库瓶颈,适合低频、非核心场景;
- Redis单实例SETNX+EXPIRE:性能高、响应快,但存在原子性缺陷(设值和设过期分两步,可能锁成功却没设上过期时间,导致死锁);
- Redis Lua脚本原子操作 + 看门狗续期:用Lua保证setnx+expire原子执行,客户端主动续期避免误释放,是目前中小规模系统的主流选择;
- ZooKeeper临时顺序节点:基于强一致的ZAB协议,天然支持锁释放监听和公平排队,适合金融级强一致性要求场景,但运维复杂、延迟略高;
- Redlock算法(多Redis实例):试图通过多数派投票提升容错,但因时钟漂移、网络分区等现实问题被Redis作者官方质疑,生产环境慎用。
实际落地的关键细节
光选对方案还不够,几个关键点常被忽略:
- 锁key必须具备业务粒度唯一性,例如"lock:order:10023"比"lock:order"更安全,避免不同订单互相阻塞;
- value必须全局唯一(如UUID或Snowflake ID),防止A误删B持有的锁;
- 加锁必须设置合理超时时间,既要覆盖最长业务耗时,又不能过长影响故障恢复;
- 释放锁必须用Lua脚本校验value再删除,杜绝“删错锁”风险;
- 建议封装重试+自旋逻辑,而非简单失败抛异常,提升请求成功率。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











