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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 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
相关文章
|
1月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
1月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
2天前
|
Java Maven
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
在Java项目中,启动jar包时遇到“no main manifest attribute”错误,且打包大小明显偏小。常见原因包括:1) Maven配置中跳过主程序打包;2) 缺少Manifest文件或Main-Class属性。解决方案如下:
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
|
20天前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
104 26
|
1月前
|
XML Java 测试技术
从零开始学 Maven:简化 Java 项目的构建与管理
Maven 是一个由 Apache 软件基金会开发的项目管理和构建自动化工具。它主要用在 Java 项目中,但也可以用于其他类型的项目。
54 1
从零开始学 Maven:简化 Java 项目的构建与管理
|
1月前
|
Java
Java项目中高精度数值计算:为何BigDecimal优于Double
在Java项目开发中,涉及金额计算、面积计算等高精度数值操作时,应选择 `BigDecimal` 而非 `Double`。`BigDecimal` 提供任意精度的小数运算、多种舍入模式和良好的可读性,确保计算结果的准确性和可靠性。例如,在金额计算中,`BigDecimal` 可以精确到小数点后两位,而 `Double` 可能因精度问题导致结果不准确。
|
1月前
|
Java Android开发
Eclipse 创建 Java 项目
Eclipse 创建 Java 项目
48 4
|
1月前
|
SQL Java 数据库连接
从理论到实践:Hibernate与JPA在Java项目中的实际应用
本文介绍了Java持久层框架Hibernate和JPA的基本概念及其在具体项目中的应用。通过一个在线书店系统的实例,展示了如何使用@Entity注解定义实体类、通过Spring Data JPA定义仓库接口、在服务层调用方法进行数据库操作,以及使用JPQL编写自定义查询和管理事务。这些技术不仅简化了数据库操作,还显著提升了开发效率。
47 3
|
1月前
|
前端开发 Java 数据库
如何实现一个项目,小白做项目-java
本教程涵盖了从数据库到AJAX的多个知识点,并详细介绍了项目实现过程,包括静态页面分析、数据库创建、项目结构搭建、JSP转换及各层代码编写。最后,通过通用分页和优化Servlet来提升代码质量。
61 1
|
1月前
|
存储 NoSQL Java
Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?
【10月更文挑战第29天】Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?
100 1