全局锁和同步阻塞

全局锁 vs 同步阻塞 (synchronized) 完整对比

一、基础概念

1. synchronized 同步阻塞(JVM 本地锁)

属于进程内锁,只在单个 JVM 内部生效,控制多线程并发竞争资源。

两种用法
java 复制代码
// 1. 对象锁(实例锁)
public synchronized void test(){}

// 2. 类锁(全局类锁,当前JVM内所有实例共用)
public static synchronized void test(){}

// 代码块锁
synchronized (this / 类名.class / 自定义对象) {
}
特点
  1. 作用范围:仅当前 JVM 进程,多台服务器、多实例之间不生效;
  2. 阻塞方式:悲观阻塞,抢不到锁线程直接阻塞;
  3. 锁存储:JVM 对象头,内存中,重启 / 宕机锁直接消失;
  4. 粒度:本地线程并发控制;
  5. 失效场景:集群部署、分布式微服务完全没用。

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");
}
特点
  1. 作用范围:全局所有服务实例,跨 JVM、跨机器互斥;
  2. 依赖中间件:Redis/ZK/ 数据库;
  3. 有超时防死锁、可重入、防锁失效等配套逻辑;
  4. 专门解决集群并发超卖、重复下单、定时任务重复执行等场景。

二、核心区别对照表

维度 synchronized 同步阻塞 分布式全局锁
作用域 单 JVM、单服务实例内多线程 集群所有服务、多台机器全局生效
底层载体 JVM 对象头、内存 Redis/ZK/ 数据库(外部中间件)
集群环境 失效,多实例会并发冲突 有效,全局互斥
宕机表现 JVM 退出锁自动释放 需设置过期时间,防止死锁
性能 无网络开销,极快 存在网络 IO,性能更低
适用场景 单体项目本地线程并发 微服务、集群、分布式系统
可重入 原生支持 需要自己实现(Redis lua)

三、使用场景区分

什么时候用 synchronized(同步阻塞)

  1. 单体项目,无集群部署,仅控制本地多线程;
  2. 接口内局部变量、本地缓存、内存变量并发读写;
  3. 定时任务仅单实例部署,不需要防重复;
  4. 简单本地计数、本地缓存更新。

示例:单体项目本地缓存计数

java 复制代码
private Integer count = 0;
public synchronized void addCount(){
    count++;
}

什么时候必须用全局分布式锁

  1. 微服务集群部署,多实例同时操作同一数据库资源(商品库存、订单);
  2. 防止超卖、重复下单、重复支付;
  3. 分布式定时任务,多实例同时触发,只允许一台执行;
  4. 跨服务共享资源竞争(文件、第三方接口、库存);
  5. 使用 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);
}

四、关键坑点

  1. 集群环境下 synchronized 完全无效 两台服务器同时请求,各自持有自己 JVM 的锁,互不感知,会并发操作数据库,出现超卖、数据错乱。
  2. 分布式锁不能替代本地锁 拿到全局锁后,方法内部多线程并发仍会有问题,极端场景需要 分布式锁 + synchronized 双重控制。
  3. synchronized 阻塞是重量级锁,高并发大量线程等待会造成请求堆积;
  4. 分布式锁存在网络延迟、锁超时、锁失效等问题,需要设计续命、防误删逻辑。

五、简单总结

  1. 同步阻塞 synchronized:本地线程锁,只锁当前服务,单体用;
  2. 全局分布式锁:跨机器跨实例锁,集群 / 微服务必用;
  3. 单体单实例:synchronized 足够;集群多实例:必须全局锁。