史上最全的java分布式锁的5种实现方式

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 史上最全的java分布式锁的5种实现方式

image.png

基于Redis实现分布式锁
Redis是一个高性能的内存数据库,支持分布式锁的实现。基于Redis实现分布式锁的步骤如下:

(1)获取Redis连接

(2)使用setnx命令设置键值对,如果返回值为1,则表示获取锁成功,否则获取锁失败

(3)如果获取锁失败,则使用get命令获取锁的值,并判断当前时间是否大于锁的超时时间,如果是,则使用getset命令设置新的锁值,并判断返回的值是否与获取的值相等,如果相等,则表示获取锁成功,否则获取锁失败

(4)使用del命令删除锁

示例代码如下:

public class RedisLock {
    
    private Jedis jedis;
    private String lockKey;
    private int expireTime = 30000;
    private int timeout = 10000;
    private boolean locked = false;
    
    public RedisLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
    }
    
    public boolean lock() throws InterruptedException {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeout) {
            long expires = System.currentTimeMillis() + expireTime + 1;
            String expiresStr = String.valueOf(expires);
            if (jedis.setnx(lockKey, expiresStr) == 1) {
                locked = true;
                return true;
            }
            String currentValueStr = jedis.get(lockKey);
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                String oldValueStr = jedis.getSet(lockKey, expiresStr);
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    locked = true;
                    return true;
                }
            }
            Thread.sleep(1000);
        }
        return false;
    }
    
    public void unlock() {
        if (locked) {
            jedis.del(lockKey);
            locked = false;
        }
    }
}

基于ZooKeeper实现分布式锁
ZooKeeper是一个分布式协调服务,支持分布式锁的实现。基于ZooKeeper实现分布式锁的步骤如下:

(1)创建一个ZooKeeper客户端连接

(2)使用create命令创建一个临时节点,如果创建成功,则表示获取锁成功,否则获取锁失败

(3)如果获取锁失败,则使用exists命令监听锁节点的删除事件,并等待锁释放

(4)使用delete命令删除锁节点

示例代码如下:

public class ZooKeeperLock implements Watcher {
    
    private ZooKeeper zooKeeper;
    private String lockPath;
    private String lockNode;
    private CountDownLatch countDownLatch = new CountDownLatch(1);
    private boolean locked = false;
    
    public ZooKeeperLock(ZooKeeper zooKeeper, String lockPath) {
        this.zooKeeper = zooKeeper;
        this.lockPath = lockPath;
    }
    
    public boolean lock() throws InterruptedException, KeeperException {
        lockNode = zooKeeper.create(lockPath + "/lock-", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        List<String> nodes = zooKeeper.getChildren(lockPath, false);
        Collections.sort(nodes);
        if (lockNode.equals(lockPath + "/" + nodes.get(0))) {
            locked = true;
            return true;
        } else {
            String prevNode = lockPath + "/" + nodes.get(Collections.binarySearch(nodes, lockNode.substring(lockNode.lastIndexOf("/") + 1)) - 1);
            zooKeeper.exists(prevNode, this);
            countDownLatch.await();
            return locked;
        }
    }
    
    public void unlock() throws InterruptedException, KeeperException {
        zooKeeper.delete(lockNode, -1);
        locked = false;
    }
    
    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeDeleted) {
            countDownLatch.countDown();
        }
    }
}

基于数据库实现分布式锁
数据库可以通过加锁机制来实现分布式锁。基于数据库实现分布式锁的步骤如下:

(1)使用select for update命令查询锁,如果查询结果为空,则表示获取锁成功,否则获取锁失败

(2)如果获取锁失败,则使用select命令查询锁,并设置超时时间,等待锁释放

(3)使用update命令更新锁

示例代码如下:

public class DatabaseLock {
    
    private Connection connection;
    private String lockTable;
    private String lockName;
    private int expireTime = 30000;
    private int timeout = 10000;
    private boolean locked = false;
    
    public DatabaseLock(Connection connection, String lockTable, String lockName) {
        this.connection = connection;
        this.lockTable = lockTable;
        this.lockName = lockName;
    }
    
    public boolean lock() throws SQLException, InterruptedException {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeout) {
            PreparedStatement selectStatement = connection.prepareStatement("SELECT * FROM " + lockTable + " WHERE name = ? FOR UPDATE");
            selectStatement.setString(1, lockName);
            ResultSet resultSet = selectStatement.executeQuery();
            if (!resultSet.next()) {
                PreparedStatement insertStatement = connection.prepareStatement("INSERT INTO " + lockTable + " (name, expires) VALUES (?, ?)");
                insertStatement.setString(1, lockName);
                insertStatement.setTimestamp(2, new Timestamp(System.currentTimeMillis() + expireTime));
                insertStatement.executeUpdate();
                locked = true;
                return true;
            }
            Timestamp expires = resultSet.getTimestamp("expires");
            if (expires != null && expires.getTime() < System.currentTimeMillis()) {
                PreparedStatement updateStatement = connection.prepareStatement("UPDATE " + lockTable + " SET expires = ? WHERE name = ? AND expires = ?");
                updateStatement.setTimestamp(1, new Timestamp(System.currentTimeMillis() + expireTime));
                updateStatement.setString(2, lockName);
                updateStatement.setTimestamp(3, expires);
                int affectedRows = updateStatement.executeUpdate();
                if (affectedRows > 0) {
                    locked = true;
                    return true;
                }
            }
            Thread.sleep(1000);
        }
        return false;
    }
    
    public void unlock() throws SQLException {
        if (locked) {
            PreparedStatement deleteStatement = connection.prepareStatement("DELETE FROM " + lockTable + " WHERE name = ?");
            deleteStatement.setString(1, lockName);
            deleteStatement.executeUpdate();
            locked = false;
        }
    }
}

基于文件系统实现分布式锁
文件系统可以通过文件锁来实现分布式锁。基于文件系统实现分布式锁的步骤如下:

(1)使用FileChannel的tryLock方法获取文件锁,如果获取锁成功,则表示获取锁成功,否则获取锁失败

(2)如果获取锁失败,则使用FileChannel的lock方法获取文件锁,并等待锁释放

(3)使用FileChannel的release方法释放文件锁

示例代码如下:

public class FileLock {
    
    private FileChannel fileChannel;
    private FileLock lock;
    private boolean locked = false;
    
    public FileLock(File file) throws IOException {
        fileChannel = new RandomAccessFile(file, "rw").getChannel();
    }
    
    public boolean lock() throws IOException, InterruptedException {
        lock = fileChannel.tryLock();
        if (lock != null) {
            locked = true;
            return true;
        } else {
            lock = fileChannel.lock();
            lock = fileChannel.tryLock();
            locked = true;
            return true;
        }
    }
    
    public void unlock() throws IOException {
        if (locked) {
            lock.release();
            fileChannel.close();
            locked = false;
        }
    }
}

基于Spring实现分布式锁
Spring提供了分布式锁的实现,可以使用Spring进行分布式锁的操作。基于Spring实现分布式锁的步骤如下:

(1)使用@Lock注解标注需要加锁的方法

(2)使用@LockKey注解标注锁的键值

(3)使用@EnableLock注解开启分布式锁

示例代码如下:

@EnableLock
public class SpringLock {
    
    @Lock(key = "lockKey")
    public void doSomething() {
        // do something
    }
    
    @Lock(key = "#id")
    public void doSomething(@LockKey String id) {
        // do something
    }
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5天前
|
监控 数据可视化 Java
【JAVA】分布式链路追踪技术概论
【JAVA】分布式链路追踪技术概论
18 2
|
29天前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
1天前
|
缓存 监控 负载均衡
Java一分钟之-Ehcache:分布式缓存系统
【6月更文挑战第17天】**Ehcache是Java的开源缓存库,支持本地和分布式缓存,提供负载均衡、数据复制和容错能力。常见问题包括网络分区导致的数据不一致、缓存雪崩和配置不当引起的性能瓶颈。解决策略涉及选择强一致性策略、设置合理缓存过期时间和监控调整配置。使用Ehcache需添加相关依赖,并配置分布式缓存,如示例所示,通过CacheManager创建和管理缓存。实践中,持续监控和优化配置至关重要。**
16 1
|
7天前
|
消息中间件 监控 Java
Java一分钟之-Kafka:分布式消息队列
【6月更文挑战第11天】Apache Kafka是一款高性能的消息队列,适用于大数据处理和实时流处理,以发布/订阅模型和分布式设计处理大规模数据流。本文介绍了Kafka基础,包括生产者、消费者、主题和代理,以及常见问题:分区选择、偏移量管理和监控不足。通过Java代码示例展示了如何创建生产者和消费者。理解并妥善处理这些问题,结合有效的监控和配置优化,是充分发挥Kafka潜力的关键。
14 0
|
19天前
|
Java 持续交付 API
Java的分布式系统与微服务架构
Java的分布式系统与微服务架构
|
21天前
|
存储 缓存 负载均衡
基于Java的分布式缓存系统设计与实现
基于Java的分布式缓存系统设计与实现
31 1
|
27天前
|
消息中间件 存储 Java
Java分布式技术面试总结(全面,实时更新)
Java分布式技术面试总结(全面,实时更新)
|
28天前
|
存储 缓存 监控
Java一分钟之-Apache Ignite:分布式内存计算平台
【5月更文挑战第21天】Apache Ignite是一款开源的分布式内存计算平台,涉及内存数据网格、流处理和计算服务。本文关注其常见问题,如数据丢失、分区不均、内存管理和网络延迟。为保证数据一致性,建议使用适当的數據模式和备份策略,实现数据持久化。优化内存配置和监控网络可提升性能与稳定性。提供的Java代码示例展示了如何创建分区缓存并设置备份。正确配置和管理Ignite是构建高可用、高性能应用的关键,持续监控集群状态至关重要。
45 0
|
28天前
|
缓存 监控 Java
Java一分钟之-Apache Geode:分布式内存数据平台
【5月更文挑战第21天】Apache Geode是低延迟的分布式内存数据平台,用于构建实时应用,提供缓存、数据库和消息传递功能。本文聚焦于Geode的常见问题,如数据一致性(数据同步延迟和分区冲突)和性能瓶颈(网络延迟和资源管理不当),并提出解决方案。确保数据一致性可通过选择合适的数据策略和利用`InterestPolicy`、`CacheListener`;提升性能则需优化网络和合理配置资源。通过示例代码展示了如何创建和操作Geode的Region。正确配置和调优Geode对于实现高可用、高性能应用至关重要。
46 1
|
1月前
|
监控 数据可视化 Java
【JAVA】分布式链路追踪技术概论
skywalking拥有更加的强大和细粒度的图形监控界面。
34 2