JAVA:常见分布式锁实现的技术指南

1、简述

分布式锁是分布式系统中用来控制资源访问的一种机制,主要解决多实例、多线程同时访问共享资源时的并发问题。本文将介绍常见的分布式锁实现方案,包括基于数据库、Redis、ZooKeeper 的实现方式,并提供实践样例。

 

2、分布式锁的基本要求

互斥性:同一时刻只能有一个客户端获取锁。

防死锁:即使客户端发生故障,锁也能自动释放。

可重入性:一个线程在持有锁时可以重复获取。

高性能:锁的获取和释放需要高效。

 

 

3、常见分布式锁实现方案

3.1 基于数据库实现分布式锁

利用数据库的唯一约束或 SELECT FOR UPDATE 特性实现分布式锁。

package com.example.springbootclient.lock;
import javax.sql.DataSource;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;
public class DatabaseDistributedLock {
    private final DataSource dataSource;
    public DatabaseDistributedLock(DataSource dataSource) {        this.dataSource = dataSource;    }
    public boolean tryLock(String lockKey, long expireTime) {        String sql = "INSERT INTO distributed_locks (lock_key, expire_time) VALUES (?, ?) ON DUPLICATE KEY UPDATE expire_time=?";        try (Connection conn = dataSource.getConnection();             PreparedStatement stmt = conn.prepareStatement(sql)) {            stmt.setString(1, lockKey);            stmt.setLong(2, System.currentTimeMillis() + expireTime);            stmt.setLong(3, System.currentTimeMillis() + expireTime);            return stmt.executeUpdate() > 0;        } catch (SQLException e) {            e.printStackTrace();            return false;        }    }
    public void releaseLock(String lockKey) {        String sql = "DELETE FROM distributed_locks WHERE lock_key = ?";        try (Connection conn = dataSource.getConnection();             PreparedStatement stmt = conn.prepareStatement(sql)) {            stmt.setString(1, lockKey);            stmt.executeUpdate();        } catch (SQLException e) {            e.printStackTrace();        }    }}

优点

简单易用,适合小规模分布式应用。

缺点

性能较差,数据库压力大。

需要手动清理过期锁。

 

3.2 基于 Redis 实现分布式锁

首先,在 Spring Boot 项目中引入 Jedis 依赖。你可以在 pom.xml 文件中添加以下内容:

<dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>4.4.3</version> <!-- 请根据实际需求选择最新版本 --></dependency>

 

利用 Redis 的 SET NX 命令实现互斥锁,并结合 EXPIRE 设置超时时间。

package com.example.springbootclient.lock;
import redis.clients.jedis.Jedis;import redis.clients.jedis.params.SetParams;
public class RedisDistributedLock {
    private final Jedis jedis;
    public RedisDistributedLock(Jedis jedis) {        this.jedis = jedis;    }
    public boolean tryLock(String lockKey, String requestId, long expireTime) {        SetParams   setParams = new SetParams();        setParams.nx();        setParams.px(expireTime);        String result = jedis.set(lockKey, requestId, setParams);        return "OK".equals(result);    }
    public boolean releaseLock(String lockKey, String requestId) {        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +                "return redis.call('del', KEYS[1]) else return 0 end";        Object result = jedis.eval(luaScript, 1, lockKey, requestId);        return "1".equals(result.toString());    }}

优点

高性能,适合高并发场景。

易于扩展和部署。

缺点

实现稍复杂,需要保证锁释放的原子性

 

3.3 基于 ZooKeeper 实现分布式锁

首先,在 Spring Boot 项目中引入 ZooKeeper 依赖。你可以在 pom.xml 文件中添加以下内容:

<dependency>    <groupId>org.apache.zookeeper</groupId>    <artifactId>zookeeper</artifactId>    <version>3.8.1</version> <!-- 请根据实际需求选择最新版本 --></dependency>

 

通过 ZooKeeper 的节点特性(临时有序节点)实现分布式锁。

package com.example.springbootclient.lock;
import org.apache.zookeeper.*;import java.util.Collections;import java.util.List;
public class ZookeeperDistributedLock {
    private final ZooKeeper zk;    private final String lockBasePath;    private String currentLockPath;
    public ZookeeperDistributedLock(ZooKeeper zk, String lockBasePath) {        this.zk = zk;        this.lockBasePath = lockBasePath;    }
    public void tryLock() throws Exception {        currentLockPath = zk.create(lockBasePath + "/lock_", new byte[0],                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        while (true) {            List<String> children = zk.getChildren(lockBasePath, false);            Collections.sort(children);            if (currentLockPath.endsWith(children.get(0))) {                return; // 获取锁成功            }
            // 等待前一个节点释放            int index = children.indexOf(currentLockPath.substring(lockBasePath.length() + 1));            String previousNode = children.get(index - 1);            zk.exists(lockBasePath + "/" + previousNode, watchedEvent -> {                if (watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted) {                    synchronized (this) {                        this.notifyAll();                    }                }            });            synchronized (this) {                this.wait();            }        }    }
    public void releaseLock() throws Exception {        zk.delete(currentLockPath, -1);    }}

优点

可靠性高,天然支持故障恢复。

无需手动清理过期锁。

缺点

部署复杂,需要维护 ZooKeeper 集群。

性能不如 Redis。

 

4、总结

分布式锁在分布式系统中是重要的组件,根据具体需求和场景选择合适的实现方案尤为关键:

如果系统简单且性能要求不高,可以选择基于数据库的分布式锁。

如果性能和扩展性是首要考虑,可以选择基于 Redis 的分布式锁。

 如果系统需要强一致性和高可靠性,可以选择基于 ZooKeeper 的分布式锁。

通过实践这些分布式锁方案,可以有效解决分布式系统中的资源竞争问题,从而提升系统的可靠性和稳定性。

 

原创文章,作者:速盾高防cdn,如若转载,请注明出处:https://www.sudun.com/ask/205901.html

Like (0)
速盾高防cdn的头像速盾高防cdn
Previous 5小时前
Next 5小时前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注