全局锁 vs 同步阻塞 (synchronized) 完整对比
一、基础概念
1. synchronized 同步阻塞(JVM 本地锁)
属于进程内锁,只在单个 JVM 内部生效,控制多线程并发竞争资源。
两种用法
java
// 1. 对象锁(实例锁)
public synchronized void test(){}
// 2. 类锁(全局类锁,当前JVM内所有实例共用)
public static synchronized void test(){}
// 代码块锁
synchronized (this / 类名.class / 自定义对象) {
}
特点
- 作用范围:仅当前 JVM 进程,多台服务器、多实例之间不生效;
- 阻塞方式:悲观阻塞,抢不到锁线程直接阻塞;
- 锁存储:JVM 对象头,内存中,重启 / 宕机锁直接消失;
- 粒度:本地线程并发控制;
- 失效场景:集群部署、分布式微服务完全没用。
2. 全局锁(分布式锁)
解决多服务、多 JVM、集群环境并发竞争,跨进程全局互斥锁。 常见实现:Redis 分布式锁、Zookeeper、数据库乐观 / 悲观锁、Seata 锁。
典型 Redis 分布式锁示例
java
// 加全局锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock:goods:1001", "uid", 30, TimeUnit.SECONDS);
if (!lock) {
throw new RuntimeException("商品正在操作,请稍后");
}
try {
// 扣库存、下单等并发操作
} finally {
// 释放锁
redisTemplate.delete("lock:goods:1001");
}
特点
- 作用范围:全局所有服务实例,跨 JVM、跨机器互斥;
- 依赖中间件:Redis/ZK/ 数据库;
- 有超时防死锁、可重入、防锁失效等配套逻辑;
- 专门解决集群并发超卖、重复下单、定时任务重复执行等场景。
二、核心区别对照表
| 维度 | synchronized 同步阻塞 | 分布式全局锁 |
|---|---|---|
| 作用域 | 单 JVM、单服务实例内多线程 | 集群所有服务、多台机器全局生效 |
| 底层载体 | JVM 对象头、内存 | Redis/ZK/ 数据库(外部中间件) |
| 集群环境 | 失效,多实例会并发冲突 | 有效,全局互斥 |
| 宕机表现 | JVM 退出锁自动释放 | 需设置过期时间,防止死锁 |
| 性能 | 无网络开销,极快 | 存在网络 IO,性能更低 |
| 适用场景 | 单体项目本地线程并发 | 微服务、集群、分布式系统 |
| 可重入 | 原生支持 | 需要自己实现(Redis lua) |
三、使用场景区分
什么时候用 synchronized(同步阻塞)
- 单体项目,无集群部署,仅控制本地多线程;
- 接口内局部变量、本地缓存、内存变量并发读写;
- 定时任务仅单实例部署,不需要防重复;
- 简单本地计数、本地缓存更新。
示例:单体项目本地缓存计数
java
private Integer count = 0;
public synchronized void addCount(){
count++;
}
什么时候必须用全局分布式锁
- 微服务集群部署,多实例同时操作同一数据库资源(商品库存、订单);
- 防止超卖、重复下单、重复支付;
- 分布式定时任务,多实例同时触发,只允许一台执行;
- 跨服务共享资源竞争(文件、第三方接口、库存);
- 使用 Nginx 负载均衡,多台服务器同时访问同一接口。
示例:集群商品扣库存(不用全局锁会超卖)
java
// 分布式全局锁保证同一商品同一时间只一个线程操作
String lockKey = "stock:lock:" + goodsId;
Boolean getLock = redisTemplate.opsForValue().setIfAbsent(lockKey, userId, 10, TimeUnit.SECONDS);
if (!getLock) {
return "当前抢购人数过多";
}
try {
// 查询库存、扣减库存、创建订单
} finally {
redisTemplate.delete(lockKey);
}
四、关键坑点
- 集群环境下 synchronized 完全无效 两台服务器同时请求,各自持有自己 JVM 的锁,互不感知,会并发操作数据库,出现超卖、数据错乱。
- 分布式锁不能替代本地锁 拿到全局锁后,方法内部多线程并发仍会有问题,极端场景需要
分布式锁 + synchronized双重控制。 - synchronized 阻塞是重量级锁,高并发大量线程等待会造成请求堆积;
- 分布式锁存在网络延迟、锁超时、锁失效等问题,需要设计续命、防误删逻辑。
五、简单总结
- 同步阻塞 synchronized:本地线程锁,只锁当前服务,单体用;
- 全局分布式锁:跨机器跨实例锁,集群 / 微服务必用;
- 单体单实例:synchronized 足够;集群多实例:必须全局锁。