在分布式系统中,多个节点同时操作共享资源的情况非常普遍。为了保证数据的一致性,分布式锁 应运而生。分布式锁 是一种跨多个服务器的互斥锁,用于协调分布式环境下的资源访问。
本文将介绍 Java 实现分布式锁 的几种常见方式,并结合 Redis、Zookeeper 等框架给出代码实例,探讨如何在实际项目中运用这些技术。
分布式锁的本质是保证在分布式环境下,多个进程或者节点可以按照正确的顺序来访问共享资源,避免数据冲突。具体场景包括:
常见的分布式锁实现方式有:
下面我们分别介绍这些方法及其在 Java 中的实现。
通过创建一张 锁表,在锁定资源时插入一条记录,通过 唯一约束 来确保同一时间只有一个节点能成功插入数据。
CREATE TABLE distributed_lock (
lock_name VARCHAR(64) PRIMARY KEY,
locked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Java 实现:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class DatabaseLockService {
private final JdbcTemplate jdbcTemplate;
public DatabaseLockService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public boolean acquireLock(String lockName) {
try {
jdbcTemplate.update("INSERT INTO distributed_lock (lock_name) VALUES (?)", lockName);
return true;
} catch (Exception e) {
return false; // 如果插入失败,表示锁已经被占用
}
}
public void releaseLock(String lockName) {
jdbcTemplate.update("DELETE FROM distributed_lock WHERE lock_name = ?", lockName);
}
}
注意事项:
Redis 是分布式锁最常用的实现之一,主要依赖于 Redis 提供的 SETNX(SET if Not Exists)命令,来确保只有一个客户端能成功设置某个键值。
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisLockService {
private final StringRedisTemplate redisTemplate;
public RedisLockService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean acquireLock(String lockKey, String lockValue, long timeout) {
Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, timeout, TimeUnit.SECONDS);
return success != null && success;
}
public void releaseLock(String lockKey, String lockValue) {
String currentValue = redisTemplate.opsForValue().get(lockKey);
if (lockValue.equals(currentValue)) {
redisTemplate.delete(lockKey); // 删除锁
}
}
}
注意事项:
Redisson 是 Redis 官方推荐的 Java 客户端,提供了原生的分布式锁实现。
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedissonLockService {
private final RedissonClient redissonClient;
public RedissonLockService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(10, TimeUnit.SECONDS); // 获取锁并设置过期时间
}
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
优点:
Zookeeper 是一个分布式协调服务,天生支持分布式锁的实现。Zookeeper 中的 临时节点 和 顺序节点 是实现分布式锁的关键。
通过 Zookeeper 的 临时顺序节点 实现锁机制,保证只有第一个创建的节点可以获取锁,其他节点需要监听前一个节点的删除事件。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class ZookeeperLockService {
private final ZooKeeper zooKeeper;
private String lockPath;
public ZookeeperLockService(String zkAddress) throws IOException {
zooKeeper = new ZooKeeper(zkAddress, 3000, null);
}
public boolean acquireLock(String lockName) throws Exception {
lockPath = zooKeeper.create("/locks/" + lockName + "/lock_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
List<String> children = zooKeeper.getChildren("/locks/" + lockName, false);
Collections.sort(children);
String smallestChild = children.get(0);
if (lockPath.endsWith(smallestChild)) {
return true; // 获取锁成功
} else {
// 等待前一个节点删除
int previousNodeIndex = Collections.binarySearch(children, lockPath.substring(lockPath.lastIndexOf('/') + 1)) - 1;
String previousNodePath = "/locks/" + lockName + "/" + children.get(previousNodeIndex);
Stat stat = zooKeeper.exists(previousNodePath, new LockWatcher());
return stat == null;
}
}
public void releaseLock() throws Exception {
zooKeeper.delete(lockPath, -1);
}
private class LockWatcher implements Watcher {
@Override
public void process(WatchedEvent event) {
synchronized (this) {
this.notifyAll(); // 通知等待的线程
}
}
}
}
注意事项:
分布式锁在分布式系统中是一个非常重要的工具,尤其是在处理资源竞争、任务调度等场景时。Redis 和 Zookeeper 是目前最常用的分布式锁实现方式。
在实际使用中,开发者需要根据业务需求选择合适的分布式锁方案,并合理配置锁的过期时间和重试机制,避免因锁竞争或死锁导致的系统性能下降。
分布式锁是保障分布式系统一致性的基础,而合理的锁机制设计能极大提升系统的稳定性和并发处理能力。