Java Redis分布式锁案例怎么实现

wen java案例 27

本文目录导读:

Java Redis分布式锁案例怎么实现

  1. 基于SETNX + EXPIRE (基础版)
  2. 使用Redisson (推荐方案)
  3. 使用Spring Data Redis + Lua脚本
  4. 完整的使用案例(订单防重复提交)
  5. 关键要点
  6. 注意事项

我来介绍Java实现Redis分布式锁的几种常见方案:

基于SETNX + EXPIRE (基础版)

import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
    private Jedis jedis;
    private String lockKey;
    private String lockValue;
    private int expireTime = 3000; // 锁过期时间,单位毫秒
    public RedisDistributedLock(Jedis jedis, String lockKey) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.lockValue = String.valueOf(System.currentTimeMillis());
    }
    /**
     * 获取锁
     */
    public boolean tryLock() {
        // SET NX 命令:key不存在时才设置
        String result = jedis.set(lockKey, lockValue, "NX", "PX", expireTime);
        return "OK".equals(result);
    }
    /**
     * 释放锁
     */
    public void unlock() {
        // 使用Lua脚本确保原子性操作
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) " +
                        "else return 0 end";
        jedis.eval(script, Collections.singletonList(lockKey), 
                   Collections.singletonList(lockValue));
    }
}

使用Redisson (推荐方案)

<!-- 添加依赖 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.17.7</version>
</dependency>
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedissonLockService {
    private RedissonClient redissonClient;
    public RedissonLockService() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://127.0.0.1:6379")
              .setPassword("password")
              .setDatabase(0);
        redissonClient = Redisson.create(config);
    }
    /**
     * 使用Redisson实现分布式锁
     */
    public void executeWithLock(String key, Runnable task) {
        RLock lock = redissonClient.getLock(key);
        try {
            // 尝试获取锁,最多等待100秒,锁自动释放30秒
            boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);
            if (isLocked) {
                System.out.println("获取锁成功,执行业务逻辑");
                task.run();
            } else {
                System.out.println("获取锁失败");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("锁操作被中断");
        } finally {
            // 释放锁(Redisson自动处理)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
    // 使用示例
    public void businessMethod() {
        executeWithLock("my_lock_key", () -> {
            // 需要加锁的业务逻辑
            System.out.println("执行业务操作...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

使用Spring Data Redis + Lua脚本

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class SpringRedisLock {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    // Lua脚本:原子性释放锁
    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "    return redis.call('del', KEYS[1]) " +
        "else " +
        "    return 0 " +
        "end";
    /**
     * 获取分布式锁
     * @param lockKey 锁的key
     * @param expireTime 过期时间(秒)
     * @return 锁标识(用于释放锁)
     */
    public String tryLock(String lockKey, long expireTime) {
        String lockValue = UUID.randomUUID().toString();
        Boolean success = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, lockValue, expireTime, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(success)) {
            return lockValue; // 返回锁标识
        }
        return null; // 获取锁失败
    }
    /**
     * 释放分布式锁
     * @param lockKey 锁的key
     * @param lockValue 锁标识
     * @return 是否释放成功
     */
    public boolean unlock(String lockKey, String lockValue) {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(UNLOCK_SCRIPT);
        redisScript.setResultType(Long.class);
        Long result = redisTemplate.execute(
            redisScript, 
            Collections.singletonList(lockKey), 
            lockValue
        );
        return Long.valueOf(1).equals(result);
    }
    // 使用示例
    public void businessWithLock() {
        String lockKey = "business_lock";
        String lockValue = tryLock(lockKey, 30);
        if (lockValue != null) {
            try {
                // 获取锁成功,执行业务逻辑
                System.out.println("执行业务操作...");
            } finally {
                // 释放锁
                unlock(lockKey, lockValue);
            }
        } else {
            System.out.println("获取锁失败,稍后重试");
        }
    }
}

完整的使用案例(订单防重复提交)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Jedis;
@Service
public class OrderService {
    @Autowired
    private JedisPool jedisPool;
    /**
     * 创建订单(防重复提交)
     */
    public String createOrder(String userId, String orderId) {
        String lockKey = "order_lock:" + userId;
        String lockValue = orderId;
        Jedis jedis = jedisPool.getResource();
        try {
            // 尝试获取锁,设置过期时间10秒
            String result = jedis.set(lockKey, lockValue, "NX", "EX", 10);
            if (!"OK".equals(result)) {
                return "订单正在处理中,请勿重复提交";
            }
            // 检查是否重复提交(业务逻辑)
            // ...
            // 执行业务逻辑
            Thread.sleep(3000);
            return "订单创建成功";
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "订单处理中断";
        } finally {
            // 安全释放锁
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                           "return redis.call('del', KEYS[1]) " +
                           "else return 0 end";
            jedis.eval(script, 
                      Collections.singletonList(lockKey), 
                      Collections.singletonList(lockValue));
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

关键要点

  1. 原子性:使用Redis的NX + PX/EX参数确保原子操作
  2. 锁标识:使用UUID等唯一标识防止误删其他线程的锁
  3. 过期时间:设置合理的过期时间防止死锁
  4. Lua脚本:释放锁时使用Lua脚本确保原子性
  5. 推荐Redisson:生产环境建议使用Redisson,它提供了更完善的实现

注意事项

  • 锁超时要根据业务执行时间合理设置
  • 考虑Redis集群下的一致性问题
  • 高并发场景下注意性能优化
  • 业务代码做好重试和降级处理

抱歉,评论功能暂时关闭!