如何在Java项目中实现分布式锁

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
云原生网关 MSE Higress,422元/月
简介: 如何在Java项目中实现分布式锁

如何在Java项目中实现分布式锁

在分布式系统中,多个节点同时访问共享资源时,为了保证数据的一致性和避免冲突,通常需要使用分布式锁。分布式锁能够确保在任何时候,只有一个节点能够对共享资源进行操作,从而避免数据错乱和并发问题。本文将深入探讨在Java项目中实现分布式锁的几种常见方法及其实现细节。

基于数据库实现分布式锁

一种简单而有效的分布式锁实现方式是基于数据库。通过在数据库中创建一张表,利用数据库的事务和唯一索引特性来确保同一时间只有一个客户端可以获取锁。

package cn.juwatech.lock;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DatabaseLock {
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";
    private static final String USER = "username";
    private static final String PASSWORD = "password";
    private static final String LOCK_SQL = "INSERT INTO distributed_lock (lock_name) VALUES (?)";
    private static final String UNLOCK_SQL = "DELETE FROM distributed_lock WHERE lock_name = ?";
    public boolean acquireLock(String lockName) {
        try (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             PreparedStatement stmt = conn.prepareStatement(LOCK_SQL)) {
            stmt.setString(1, lockName);
            int rowsAffected = stmt.executeUpdate();
            return rowsAffected > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    public boolean releaseLock(String lockName) {
        try (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             PreparedStatement stmt = conn.prepareStatement(UNLOCK_SQL)) {
            stmt.setString(1, lockName);
            int rowsAffected = stmt.executeUpdate();
            return rowsAffected > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
}

在上述示例中,我们通过执行INSERT语句来尝试获取锁,并通过DELETE语句释放锁。需要注意的是,基于数据库的锁虽然简单易懂,但在高并发场景下性能可能成为瓶颈,因此需要谨慎选择数据库和优化SQL语句。

基于Redis实现分布式锁

Redis作为一个高性能的内存数据存储系统,也是实现分布式锁的常用选择。Redis提供了SETNX(SET if Not eXists)命令,可以原子性地设置键值对,因此可以用来实现分布式锁。

package cn.juwatech.lock;
import redis.clients.jedis.Jedis;
public class RedisLock {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;
    private static final String LOCK_KEY = "distributed_lock";
    private static final int LOCK_EXPIRE_TIME = 30000; // 锁的过期时间,单位毫秒
    private Jedis jedis;
    public RedisLock() {
        this.jedis = new Jedis(REDIS_HOST, REDIS_PORT);
    }
    public boolean acquireLock() {
        String result = jedis.set(LOCK_KEY, "locked", "NX", "PX", LOCK_EXPIRE_TIME);
        return "OK".equals(result);
    }
    public void releaseLock() {
        jedis.del(LOCK_KEY);
    }
}

在以上示例中,我们使用了Redis的set命令来尝试获取锁,并设置了过期时间,这样即使锁未被释放,也能在一定时间后自动释放。

基于ZooKeeper实现分布式锁

ZooKeeper作为一个分布式协调服务,也可以用来实现分布式锁。通过创建有序临时节点,利用其顺序特性来实现锁的竞争和释放。

package cn.juwatech.lock;
import org.apache.zookeeper.*;
public class ZooKeeperLock implements Watcher {
    private static final String ZOOKEEPER_HOST = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;
    private ZooKeeper zooKeeper;
    private String lockPath;
    public ZooKeeperLock() throws Exception {
        this.zooKeeper = new ZooKeeper(ZOOKEEPER_HOST, SESSION_TIMEOUT, this);
    }
    public boolean acquireLock() throws KeeperException, InterruptedException {
        // 创建临时有序节点
        lockPath = zooKeeper.create("/locks/node_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
        // 检查是否是最小节点
        String[] nodes = zooKeeper.getChildren("/locks", false).toArray(new String[0]);
        for (String node : nodes) {
            if (lockPath.endsWith(node)) {
                return true;
            }
        }
        return false;
    }
    public void releaseLock() throws InterruptedException, KeeperException {
        zooKeeper.delete(lockPath, -1);
    }
    @Override
    public void process(WatchedEvent event) {
        // 实现Watcher接口的方法
    }
}

在上述示例中,我们使用ZooKeeper的临时有序节点来实现锁,通过创建节点的顺序来判断是否获得锁。

分布式锁的选择与总结

不同的分布式锁实现方案各有优缺点,应根据具体的业务场景和性能需求选择合适的实现方式。基于数据库的锁简单易懂,适合对一致性要求不高的场景;基于Redis和ZooKeeper的实现方式更适合对性能和一致性有高要求的场景。

通过本文的介绍,希望读者能够深入理解分布式锁的工作原理及其在Java项目中的实现方式,并能在实际应用中进行灵活应用和调整。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3天前
|
前端开发 Java 编译器
【前端学java】java基础练习缺少项目?看这篇文章就够了!(完结)
【8月更文挑战第11天】java基础练习缺少项目?看这篇文章就够了!(完结)
11 0
|
4天前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
|
4天前
|
SQL Java 数据库连接
java连接数据库加载驱动到java项目
该博客文章介绍了如何在Java项目中通过代码加载数据库驱动并连接SQL Server数据库,包括具体的加载驱动和建立数据库连接的步骤,以及如何将驱动包添加到Java项目的构建路径中。
|
8天前
|
消息中间件 Java Kafka
"Kafka快速上手:从环境搭建到Java Producer与Consumer实战,轻松掌握分布式流处理平台"
【8月更文挑战第10天】Apache Kafka作为分布式流处理平台的领头羊,凭借其高吞吐量、可扩展性和容错性,在大数据处理、实时日志收集及消息队列领域表现卓越。初学者需掌握Kafka基本概念与操作。Kafka的核心组件包括Producer(生产者)、Broker(服务器)和Consumer(消费者)。Producer发送消息到Topic,Broker负责存储与转发,Consumer则读取这些消息。首先确保已安装Java和Kafka,并启动服务。接着可通过命令行创建Topic,并使用提供的Java API实现Producer发送消息和Consumer读取消息的功能。
30 8
|
3天前
|
资源调度 Java 调度
项目环境测试问题之Schedulerx2.0通过分布式分片任务解决单机计算瓶颈如何解决
项目环境测试问题之Schedulerx2.0通过分布式分片任务解决单机计算瓶颈如何解决
项目环境测试问题之Schedulerx2.0通过分布式分片任务解决单机计算瓶颈如何解决
|
3天前
|
前端开发 Java 编译器
【前端学java】java基础练习缺少项目?看这篇文章就够了!(17)
【8月更文挑战第11天】java基础练习缺少项目?看这篇文章就够了!
10 0
【前端学java】java基础练习缺少项目?看这篇文章就够了!(17)
|
21天前
|
Java Spring
idea新建spring boot 项目右键无package及java类的选项
idea新建spring boot 项目右键无package及java类的选项
33 5
|
3天前
|
存储 缓存 开发框架
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
|
3天前
|
Java Spring
Java SpringBoot Bean InitializingBean 项目初始化
Java SpringBoot Bean InitializingBean 项目初始化
10 0
|
4天前
|
Java 网络安全 开发工具
新手入门Java。如何下载Eclipse、写出最基本的“Hello word”以及如何连接github并且上传项目。
新手入门Java。如何下载Eclipse、写出最基本的“Hello word”以及如何连接github并且上传项目。
11 0