Java使用Redis与RedisTemplate实现分布式锁(应用场景:防止定时任务重复执行)

分布式锁应用场景:

        1. 防止缓存击穿:在高并发场景下,如果缓存失效或者未命中,会导致大量的请求直接访问数据库,导致数据库宕机或者性能下降。使用分布式锁可以保证只有一个请求去访问数据库,其他请求等待,从而避免了缓存击穿。

        2. 防止超卖和重复下单:在电商系统中,如果多个用户同时下单同一个商品,会导致超卖和重复下单。使用分布式锁可以保证只有一个用户下单成功。另外,在秒杀和抢购场景中,使用分布式锁可以保证商品的数量不会超卖。

        3. 防止任务重复执行:在分布式任务调度场景中,可能会出现多个节点同时调度同一个任务的情况,导致任务重复执行。使用分布式锁可以保证只有一个节点去调度任务,避免任务重复执行。

        4. 保证数据一致性:在分布式事务场景中,需要保证多个节点同时访问同一个数据时的数据一致性。使用分布式锁可以保证在一个节点进行修改时,其他节点不能同时进行修改,保证了数据的一致性。

        5. 分布式限流:在高并发场景下,使用分布式锁可以实现分布式限流,保证系统不会因为请求过多而宕机。例如,可以使用 Redis 分布式锁实现分布式限流。

实现Redis高并发分布式锁的步骤如下:

  1. 使用Redis的setnx命令来尝试获取锁(利用Redis的原子性保证互斥)

  2. 如果获取锁成功,设置锁的过期时间,避免死锁

  3. 如果获取锁失败,等待一段时间后重试或者放弃

  4. 在锁的过期时间到达之前,执行完业务逻辑后释放锁

分布式环境防止任务重复执行的代码示例

具体Java代码实现如下:

package com.soilsurvey.webapi.common.core.redis;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Created with 跳跳公司.
 *
 * @Author: 小皮球
 * @Date: 2023/08/29/15:47
 * @Description:
 */
public class DistributedLock implements AutoCloseable{

    private RedisTemplate<String, String> redisTemplate;
    private String lockKey;
    private String requestId;

    public DistributedLock(RedisTemplate<String, String> redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
        this.requestId = UUID.randomUUID().toString();
    }

    /**
     * 获取锁
     * @param expireTime
     * @return
     */
    public boolean acquireLock(long expireTime) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        Boolean acquired = valueOperations.setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MINUTES);
        return acquired != null && acquired;
    }

    /**
     * 释放锁
     */
    public void releaseLock() {
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
    }

    @Override
    public void close() {
        releaseLock();
    }
}

使用方式如下:


    @Scheduled(cron = "0 1 0 * * ?")
    public void executeEveryDay() {
        try (DistributedLock lock = new DistributedLock(redisTemplate, LOCK_KEY_EVERY_DAY)) {
            // 获取锁
            if (lock.acquireLock(10)) {
                log.info("开始更新每日任务");
                // 这里放业务代码
                log.info("更新每日任务完成");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值