并发锁和分布式锁的解析

并发锁和分布式锁是解决并发问题的两种重要机制,它们既有区别又有联系。

一、核心区别对比

维度 并发锁(本地锁) 分布式锁
作用范围 单进程内,多线程之间 跨进程、跨服务、跨机器
应用场景 单机应用,多线程并发 分布式系统,多服务实例
实现原理 基于JVM内存或操作系统机制 基于外部中间件协调
性能影响 性能开销小,速度快 性能开销大,网络延迟
复杂度 实现简单,API丰富 实现复杂,需要考虑网络分区、脑裂等问题

二、技术实现对比

并发锁的实现方式

1. synchronized关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SynchronizedExample {
private int counter = 0;
private final Object lock = new Object();

// 方法级别锁
public synchronized void increment() {
counter++;
}

// 代码块锁
public void incrementWithBlock() {
synchronized(lock) {
counter++;
}
}
}

2. ReentrantLock可重入锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;

public void increment() {
lock.lock(); // 可中断、可超时
try {
counter++;
} finally {
lock.unlock(); // 必须手动释放
}
}

public boolean tryIncrement() {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
counter++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}

3. ReadWriteLock读写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ReadWriteLockExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private Map<String, String> data = new HashMap<>();

// 读操作 - 共享锁
public String get(String key) {
readLock.lock();
try {
return data.get(key);
} finally {
readLock.unlock();
}
}

// 写操作 - 排他锁
public void put(String key, String value) {
writeLock.lock();
try {
data.put(key, value);
} finally {
writeLock.unlock();
}
}
}

分布式锁的实现方式

1. Redis分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RedisDistributedLock {
private JedisPool jedisPool;

public boolean lock(String lockKey, String clientId, long expireSeconds) {
try (Jedis jedis = jedisPool.getResource()) {
// SET NX EX 保证原子性
String result = jedis.set(lockKey, clientId,
SetParams.setParams().nx().ex(expireSeconds));
return "OK".equals(result);
}
}

public boolean unlock(String lockKey, String clientId) {
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
try (Jedis jedis = jedisPool.getResource()) {
Object result = jedis.eval(script,
Collections.singletonList(lockKey),
Collections.singletonList(clientId));
return Long.valueOf(1L).equals(result);
}
}
}

2. Redisson分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RedissonLockExample {
private RedissonClient redisson;

public void processWithLock(String lockKey) {
RLock lock = redisson.getLock(lockKey);

try {
// 支持自动续期,避免业务执行时间超过锁超时时间
boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (locked) {
// 执行业务逻辑
doBusiness();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}

三、联系与协同工作

1. 层级关系

在实际系统中,两者往往协同工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class OrderService {
// 本地锁:解决单机内多线程竞争
private final ReentrantLock localLock = new ReentrantLock();

// 分布式锁:解决多服务实例竞争
@Autowired
private DistributedLock distributedLock;

public void createOrder(Order order) {
// 第一层:本地锁,快速过滤同一JVM内的并发
localLock.lock();
try {
// 第二层:分布式锁,确保集群环境下的唯一性
String lockKey = "order_create:" + order.getUserId();
if (distributedLock.tryLock(lockKey, 5, TimeUnit.SECONDS)) {
try {
// 真正的业务逻辑
doCreateOrder(order);
} finally {
distributedLock.unlock(lockKey);
}
} else {
throw new RuntimeException("订单创建过于频繁");
}
} finally {
localLock.unlock();
}
}
}

2. 设计模式结合

双重检查锁定模式(DCL)在分布式环境的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class DistributedSingleton {
// 本地 volatile 保证可见性
private volatile Object cachedData;
// 本地锁用于快速检查
private final Object localLock = new Object();

public Object getData() {
// 第一重检查:本地快速路径
if (cachedData == null) {
synchronized (localLock) {
// 第二重检查:本地锁内再次检查
if (cachedData == null) {
// 第三重检查:分布式锁确保集群唯一
String lockKey = "data_init_lock";
if (distributedLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
try {
// 最终检查
if (cachedData == null) {
cachedData = loadDataFromDB();
}
} finally {
distributedLock.unlock(lockKey);
}
}
}
}
}
return cachedData;
}
}

四、适用场景对比

适合使用并发锁的场景

场景1:单机内存计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LocalCounter {
private final AtomicLong counter = new AtomicLong(0);
private final ReentrantLock calculationLock = new ReentrantLock();

public void complexCalculation() {
calculationLock.lock();
try {
// 复杂的数学计算,需要保证计算过程的原子性
long result = performComplexMath(counter.get());
counter.set(result);
} finally {
calculationLock.unlock();
}
}
}

场景2:线程安全的集合操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ThreadSafeCollection {
private final List<String> data = new ArrayList<>();
private final ReentrantLock listLock = new ReentrantLock();

public void batchAdd(List<String> newItems) {
listLock.lock();
try {
data.addAll(newItems);
// 排序和其他操作
Collections.sort(data);
} finally {
listLock.unlock();
}
}
}

适合使用分布式锁的场景

场景1:分布式定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DistributedScheduler {
@Scheduled(cron = "0 0/5 * * * ?")
public void distributedTask() {
String lockKey = "scheduled_task:data_sync";
if (distributedLock.tryLock(lockKey, 4, TimeUnit.MINUTES)) {
try {
// 确保集群中只有一个实例执行此任务
executeDataSync();
} finally {
distributedLock.unlock(lockKey);
}
}
}
}

场景2:全局资源访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class GlobalResourceManager {
public boolean allocateResource(String resourceId, String clientId) {
String lockKey = "resource_allocation:" + resourceId;
if (distributedLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
try {
// 检查资源状态并分配
Resource resource = resourceDao.findById(resourceId);
if (resource.getStatus() == Status.AVAILABLE) {
resource.setStatus(Status.ALLOCATED);
resource.setOwner(clientId);
resourceDao.update(resource);
return true;
}
return false;
} finally {
distributedLock.unlock(lockKey);
}
}
throw new RuntimeException("资源分配失败,请重试");
}
}

五、混合使用的最佳实践

1. 本地缓存 + 分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class CachedService {
private final ConcurrentHashMap<String, Object> localCache = new ConcurrentHashMap<>();
private final ReadWriteLock cacheLock = new ReentrantReadWriteLock();

public Object getWithDistributedLock(String key) {
// 第一步:本地读锁快速返回
cacheLock.readLock().lock();
try {
Object value = localCache.get(key);
if (value != null) {
return value;
}
} finally {
cacheLock.readLock().unlock();
}

// 第二步:分布式锁保护数据加载
String distributedLockKey = "cache_load:" + key;
if (distributedLock.tryLock(distributedLockKey, 5, TimeUnit.SECONDS)) {
try {
// 双重检查
cacheLock.readLock().lock();
try {
Object value = localCache.get(key);
if (value != null) {
return value;
}
} finally {
cacheLock.readLock().unlock();
}

// 加载数据
Object loadedValue = loadFromDataSource(key);

// 更新缓存
cacheLock.writeLock().lock();
try {
localCache.put(key, loadedValue);
} finally {
cacheLock.writeLock().unlock();
}

return loadedValue;
} finally {
distributedLock.unlock(distributedLockKey);
}
}
throw new RuntimeException("系统繁忙,请重试");
}
}

2. 性能优化策略

本地快速失败 + 分布式兜底

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class OptimizedLockManager {
// 本地限流器
private final RateLimiter localRateLimiter = RateLimiter.create(1000);
private final ReentrantLock localTryLock = new ReentrantLock();

public boolean tryAcquireWithOptimization(String resourceKey) {
// 第一步:本地限流快速失败
if (!localRateLimiter.tryAcquire()) {
return false;
}

// 第二步:本地锁快速尝试
if (!localTryLock.tryLock()) {
return false;
}

try {
// 第三步:分布式锁最终控制
return distributedLock.tryLock(resourceKey, 1, TimeUnit.SECONDS);
} finally {
localTryLock.unlock();
}
}
}

六、总结

核心联系

  1. 共同目标:都是解决并发访问共享资源的问题
  2. 互补关系:在分布式系统中往往需要结合使用
  3. 设计思想:都基于互斥、可见性、有序性等并发原理

选择原则

  • 单机应用:优先使用并发锁,性能更好
  • 分布式系统:必须使用分布式锁保证跨进程一致性
  • 混合场景:使用并发锁做本地优化,分布式锁做全局保证
  • 性能敏感:尽量减少分布式锁的使用范围和时间

理解两者的区别和联系,有助于在合适的场景选择合适的技术方案,构建高性能、高可用的并发系统。

并发锁和分布式锁有各自明确的应用场景,选择正确的锁类型对系统性能和可靠性至关重要。

一、并发锁(本地锁)应用场景

1. 单机内存数据保护

场景:线程安全的计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LocalCounterService {
private final ReentrantLock counterLock = new ReentrantLock();
private final Map<String, AtomicInteger> counters = new HashMap<>();

public void increment(String counterName) {
counterLock.lock();
try {
counters.computeIfAbsent(counterName, k -> new AtomicInteger(0))
.incrementAndGet();
} finally {
counterLock.unlock();
}
}

// 适用原因:单机内存操作,无需跨进程协调
}

场景:缓存更新操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LocalCacheManager {
private final ReadWriteLock cacheLock = new ReentrantReadWriteLock();
private final Map<String, Object> cache = new ConcurrentHashMap<>();

public void refreshCache() {
cacheLock.writeLock().lock();
try {
// 全量刷新缓存
cache.clear();
cache.putAll(loadDataFromDB());
} finally {
cacheLock.writeLock().unlock();
}
}

public Object getFromCache(String key) {
cacheLock.readLock().lock();
try {
return cache.get(key);
} finally {
cacheLock.readLock().unlock();
}
}
// 适用原因:缓存只在单机内使用,读写分离提升性能
}

2. 复杂业务逻辑原子性

场景:订单状态流转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class OrderStateMachine {
private final Object orderLock = new Object();

public boolean transitionOrderState(Long orderId, OrderState from, OrderState to) {
synchronized (orderLock) {
Order order = orderDAO.findById(orderId);
if (order.getState() != from) {
return false; // 状态不匹配
}

// 复杂的状态校验逻辑
if (validateTransition(order, from, to)) {
order.setState(to);
orderDAO.update(order);
return true;
}
return false;
}
// 适用原因:单服务实例内的状态机,保证状态转换的原子性
}
}

3. 资源池管理

场景:数据库连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SimpleConnectionPool {
private final ReentrantLock poolLock = new ReentrantLock();
private final List<Connection> idleConnections = new ArrayList<>();
private final Set<Connection> activeConnections = new HashSet<>();

public Connection getConnection() throws SQLException {
poolLock.lock();
try {
if (!idleConnections.isEmpty()) {
Connection conn = idleConnections.remove(0);
activeConnections.add(conn);
return conn;
}

// 创建新连接
Connection newConn = createNewConnection();
activeConnections.add(newConn);
return newConn;
} finally {
poolLock.unlock();
}
}
// 适用原因:连接池是单进程内的资源管理
}

4. 批量操作原子性

场景:批量数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class BatchDataProcessor {
private final ReentrantLock batchLock = new ReentrantLock();
private List<Data> batchData = new ArrayList<>();

public void addToBatch(Data data) {
batchLock.lock();
try {
batchData.add(data);
if (batchData.size() >= BATCH_SIZE) {
processBatch(batchData);
batchData.clear();
}
} finally {
batchLock.unlock();
}
}
// 适用原因:单机内的批量操作,保证批次的完整性
}

二、分布式锁应用场景

1. 分布式资源竞争

场景:分布式库存扣减

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class DistributedInventoryService {
@Autowired
private DistributedLock distributedLock;

public boolean deductStock(Long productId, Integer quantity) {
String lockKey = "stock_deduction:" + productId;

if (!distributedLock.tryLock(lockKey, 3, TimeUnit.SECONDS)) {
throw new BusinessException("系统繁忙,请重试");
}

try {
Product product = productDAO.selectById(productId);
if (product.getStock() >= quantity) {
product.setStock(product.getStock() - quantity);
productDAO.updateById(product);

// 记录库存变更日志
inventoryLogService.logDeduction(productId, quantity);
return true;
}
return false;
} finally {
distributedLock.unlock(lockKey);
}
// 适用原因:多服务实例同时操作同一商品库存
}
}

2. 分布式定时任务

场景:集群任务调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class DistributedScheduler {
@Autowired
private DistributedLock distributedLock;

@Scheduled(fixedRate = 60000) // 每分钟执行
public void dataSyncTask() {
String lockKey = "task:data_sync";

// 只有获取锁的实例执行任务
if (distributedLock.tryLock(lockKey, 55, TimeUnit.SECONDS)) {
try {
log.info("开始执行数据同步任务");
dataSyncService.syncAllData();
log.info("数据同步任务完成");
} finally {
distributedLock.unlock(lockKey);
}
}
// 适用原因:防止集群中多个实例重复执行定时任务
}
}

3. 分布式唯一性控制

场景:防重复提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RestController
public class OrderController {
@Autowired
private DistributedLock distributedLock;

@PostMapping("/order/create")
public ResponseEntity createOrder(@RequestBody OrderRequest request) {
String requestId = request.getRequestId();
String lockKey = "order_create:" + requestId;

// 同一请求ID在分布式环境下只能成功一次
if (!distributedLock.tryLock(lockKey, 30, TimeUnit.SECONDS)) {
throw new BusinessException("请勿重复提交订单");
}

try {
// 检查是否已处理过该请求
if (orderService.isRequestProcessed(requestId)) {
return ResponseEntity.ok("订单已创建");
}

// 创建订单
Order order = orderService.createOrder(request);
return ResponseEntity.ok(order);
} finally {
// 可以设置较短的自动过期,防止异常情况锁无法释放
distributedLock.unlock(lockKey);
}
// 适用原因:保证分布式环境下请求的唯一性
}
}

4. 全局配置更新

场景:分布式配置管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Service
public class GlobalConfigService {
@Autowired
private DistributedLock distributedLock;

public void updateGlobalConfig(Config newConfig) {
String lockKey = "global_config_update";

if (!distributedLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
throw new BusinessException("配置更新中,请稍后重试");
}

try {
// 1. 更新数据库配置
configDAO.update(newConfig);

// 2. 清除所有节点的本地缓存
cacheEvictionService.evictAllNodes("config_cache");

// 3. 广播配置变更通知
eventPublisher.publishConfigChangeEvent(newConfig);

} finally {
distributedLock.unlock(lockKey);
}
// 适用原因:防止多个管理员同时更新全局配置导致数据不一致
}
}

5. 分布式序列生成

场景:全局唯一ID生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Service
public class DistributedIdGenerator {
@Autowired
private DistributedLock distributedLock;
@Autowired
private RedisTemplate redisTemplate;

public Long generateOrderId() {
String lockKey = "id_generator:order";
String counterKey = "order_id_counter";

if (!distributedLock.tryLock(lockKey, 2, TimeUnit.SECONDS)) {
throw new BusinessException("ID生成服务繁忙");
}

try {
// 原子性递增
Long nextId = redisTemplate.opsForValue().increment(counterKey);
return nextId;
} finally {
distributedLock.unlock(lockKey);
}
// 适用原因:保证分布式环境下ID的唯一性和连续性
}
}

三、混合使用场景

1. 多级缓存更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Service
public class MultiLevelCacheService {
// 本地锁:保护本地缓存
private final ReentrantLock localCacheLock = new ReentrantLock();
private final Map<String, Object> localCache = new HashMap<>();

// 分布式锁:保护分布式缓存和数据库
@Autowired
private DistributedLock distributedLock;

public Object getData(String key) {
// 第一层:检查本地缓存(无锁读)
Object localValue = localCache.get(key);
if (localValue != null) {
return localValue;
}

// 第二层:本地锁保护缓存加载过程
localCacheLock.lock();
try {
// 双重检查
localValue = localCache.get(key);
if (localValue != null) {
return localValue;
}

// 第三层:分布式锁保护数据源访问
String distributedLockKey = "cache_loading:" + key;
if (distributedLock.tryLock(distributedLockKey, 5, TimeUnit.SECONDS)) {
try {
// 从数据源加载
Object data = loadFromDataSource(key);

// 更新本地缓存
localCache.put(key, data);

return data;
} finally {
distributedLock.unlock(distributedLockKey);
}
}
throw new BusinessException("数据加载中,请重试");
} finally {
localCacheLock.unlock();
}
}
}

2. 分布式批处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Service
public class DistributedBatchProcessor {
// 本地锁:保护批次积累
private final ReentrantLock batchLock = new ReentrantLock();
private final Map<String, List<Data>> batches = new HashMap<>();

@Autowired
private DistributedLock distributedLock;

public void addToDistributedBatch(String batchKey, Data data) {
// 本地锁:快速积累数据
batchLock.lock();
try {
batches.computeIfAbsent(batchKey, k -> new ArrayList<>())
.add(data);

List<Data> currentBatch = batches.get(batchKey);
if (currentBatch.size() >= BATCH_SIZE) {
// 达到批次大小,尝试获取分布式锁进行处理
String distributedLockKey = "batch_process:" + batchKey;
if (distributedLock.tryLock(distributedLockKey, 10, TimeUnit.SECONDS)) {
try {
// 处理批次(保证集群中只有一个处理器在处理这个批次)
processBatch(batchKey, currentBatch);
batches.remove(batchKey);
} finally {
distributedLock.unlock(distributedLockKey);
}
}
}
} finally {
batchLock.unlock();
}
}
}

四、选择原则总结

使用并发锁当:

  • ✅ 单机应用或单服务实例
  • ✅ 操作内存数据或本地资源
  • ✅ 性能要求极高,需要低延迟
  • ✅ 不需要跨进程协调

使用分布式锁当:

  • ✅ 分布式系统,多服务实例
  • ✅ 操作共享存储(数据库、Redis等)
  • ✅ 需要全局唯一性保证
  • ✅ 跨进程的资源协调

避免使用锁当:

  • ❌ 可以通过无锁数据结构解决
  • ❌ 业务允许最终一致性
  • ❌ 读多写少且可以使用乐观锁
  • ❌ 可以通过消息队列解耦异步处理

正确选择锁类型需要在一致性要求、性能开销和系统复杂度之间做出平衡。