redis分布式锁的实现以及锁时间的延长_redis分布式锁 增加时间_西门吹雪--999的博客-程序员宅基地

技术标签: java  redis  

redis 可用于分布式事务锁:

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLock {
    


    @Autowired
    private RedisTemplate redisTemplate;


    private static Map<String, LockInfo> lockInfoMap = new ConcurrentHashMap<>();


    @Data
    public static class LockInfo {
    

        private String key;

        private String identity;

        private int expireTime;

        //更新时间
        private long renewalTime;

        //更新间隔
        private long renewalInterval;

        public static LockInfo getLockInfo(String key, String identity, int expireTime) {
    
            LockInfo lockInfo = new LockInfo();
            lockInfo.setKey(key);
            lockInfo.setIdentity(identity);
            lockInfo.setExpireTime(expireTime);
            lockInfo.setRenewalTime(System.currentTimeMillis());
            lockInfo.setRenewalInterval(expireTime * 2000 / 3);
            return lockInfo;
        }
    }


    /**
     * 获取锁
     *
     * @param lockKey    锁
     * @param identity   身份标识(保证锁不会被其他人释放)
     * @param expireTime 锁的过期时间(单位:秒)
     * @return
     */
    public boolean tryLock(String lockKey, String identity, int expireTime) {
    
        boolean ret = false;
        try {
    
            String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";

            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Object result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(), Collections.singletonList(lockKey), identity, expireTime);
            System.out.println(result + "-----------");
            //Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime + "");

            if (Long.valueOf(1L).equals(result)) {
    
                return true;
            }

            if (expireTime >= 10) {
    
                lockInfoMap.put(lockKey + identity, LockInfo.getLockInfo(lockKey, identity, expireTime));
            }

        } catch (Exception e) {
    
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 释放锁
     *
     * @param lockKey
     * @param identity
     * @return
     */
    public boolean unlock(String lockKey, String identity) {
    

        lockInfoMap.remove(lockKey + identity);

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

        Object result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(), Collections.singletonList(lockKey), identity);
        if (Long.valueOf(1L).equals(result)) {
    
            return true;
        }

        return false;
    }


    /**
     * 更新redis锁的过期时间
     *
     * @param lockKey
     * @param identity
     * @return
     */
    public boolean renewal(String lockKey, String identity, int expireTime) {
    

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('expire', KEYS[1], ARGV[2]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Object result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(), Collections.singletonList(lockKey), identity, expireTime);
        if (Long.valueOf(1L).equals(result)) {
    
            return true;
        }
        return false;
    }


    /**
     * Lua脚本
     * // 加锁
     * if
     *     redis.call('setNx',KEYS[1],ARGV[1])
     *   then
     *     if redis.call('get',KEYS[1])==ARGV[1]
     *     return redis.call('expire',KEYS[1],ARGV[2])
     *   else
     *     return 0
     *   end
     * end
     *
     * // 解锁
     *   redis.call('get', KEYS[1]) == ARGV[1]
     * then
     *   return redis.call('del', KEYS[1])
     * else
     *   return 0
     */


    /**
     * 获取锁
     *
     * @param lockKey    锁
     * @param identity   身份标识(保证锁不会被其他人释放)
     * @param expireTime 锁的过期时间(单位:秒)
     * @return
     */
    public boolean lock(String lockKey, String identity, long expireTime) {
    
        return redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);

    }

    /**
     * 释放锁
     *
     * @param lockKey  锁
     * @param identity 身份标识(保证锁不会被其他人释放)
     * @return
     */
    public boolean releaseLock(String lockKey, String identity) {
    
        String luaScript = "if " +
                "  redis.call('get', KEYS[1]) == ARGV[1] " +
                "then " +
                "  return redis.call('del', KEYS[1]) " +
                "else " +
                "  return 0 " +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Boolean.class);
        redisScript.setScriptText(luaScript);
        List<String> keys = new ArrayList<>();
        keys.add(lockKey);
        Object result = redisTemplate.execute(redisScript, keys, identity);
        return (boolean) result;
    }


    /**
     * @author wangshbv
     * 
     * @Desc 定时去检查redis锁的过期时间
     * @Param
     * @Return
     */
    @Scheduled(fixedRate = 5000L)
    @Async("redisExecutor")
    public void renewal() {
    
        long now = System.currentTimeMillis();
        for (Map.Entry<String, LockInfo> lockInfoEntry : lockInfoMap.entrySet()) {
    
            LockInfo lockInfo = lockInfoEntry.getValue();
            if (lockInfo.getRenewalTime() + lockInfo.getRenewalInterval() < now) {
    
                renewal(lockInfo.getKey(), lockInfo.getIdentity(), lockInfo.getExpireTime());
                lockInfo.setRenewalTime(now);
            }
        }
    }

    /**
     * @author wangshbv
     * @Date 2021/11/19 17:08
     * @Desc 分布式锁设置单独线程池
     * @Param
     * @Return
     */
    @Bean("redisExecutor")
    public Executor redisExecutor() {
    
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.setQueueCapacity(1);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("redis-renewal-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        return executor;
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37937394/article/details/121436603

智能推荐

一台电脑同时上内网和外网_两台电脑之间使用内网通该如何设置_青碧凝霜的博客-程序员宅基地

大家经常遇见有的公司设定了内网和外网,但是有的时候你又要去外网查询数据。我不装了,我吐了。我又不是营销号——————————————————————————————今天有个需求,同时上内网和外网。然后求助于万能的百度,百度提示用ROUTE。就是静态网站+ROUTE。给大家说:这个是没用的。这群人一抄二、二抄十,然后所有的人都觉得这个是个好方法。但是问题是:系统会自己维护一个路由表啊!!!!!!!也就是说:当上不上网的时候,系统会自己往路由表写信息,让你安心上网。所以——你会发现:你什么

Powerdesigner连接MySQL数据库并将物理模型导入数据库,包含视图的设置_怎么将物理文件导入到数据库_Joy_joye的博客-程序员宅基地

一、连接MySQL数据库的相关配置1,首先需要安装MySQL的连接驱动:mysql-connector-odbc-5.1.13-win32;由于我安装的MySQL版本是5.1版本的,因此,直接在百度上进行搜索mysql-connector-odbc-5.1.13-win32,得到官网链接如下,点击进行下载。下载后直接安装即可。2,安装完成后,点击database 再点击conn...

集成公告|预言机平台DIA与Moonbeam达成合作,通过Moonriver集成至Kusama生态系统_moonriver生态_Moonbeam Community的博客-程序员宅基地

DIA Protocol成功在领先的全兼容以太坊Kusama平行链Moonriver网络启用,用户可以通过如下链接查看合约地址以及相关记录。DIA可精准筛选且验证的喂价,为Moonbeam的先行网络注入活力。目前,预言机提供主流资产等相关的数据资料,未来将持续扩增资产种类。本次集成使DApp能够在部署在Moonbeam之前直接使用可信任的数据,并促进平行链特定应用场景的发展。查看合约地址以及相关交易记录:https://blockscout.moonriver.moonbeam.

Unity3D项目之 Survival Shooter 记录_aishaodu0717的博客-程序员宅基地

1.导入资源 2.把预设文件的环境拖到场景中, 3.位置归0 4.保存场景 5.删除默认灯光,把预设灯光拖到场景中,位置归0 6.新建一个 Quad 7.旋转90度,设置缩放100,100,1 重命名为floor 移除它的MeshRender 组件,并设在在Floor层 8.保存场景 9.新建一个空物体,重命名为BGM ...

解密京东618技术:重构多中心交易平台 11000个Docker支撑_NullPointerExcept的博客-程序员宅基地

电商平台的促销活动往往意味着技术系统的大升级。今年的618周年大促,京东实现了商品中心、用户中心和交易中心等平台化升级。在日前的京东技术开放日618技术分享专场,多位京东技术专家联袂解析了京东的技术研发体系如何在高强度的负载压力下,保证业务系统的平稳运行,并介绍大型互联网平台技术升级、备战思路、应急预案设计、问题应对等各方面的实战经验。专家们介绍,京东的技术架构是由规模驱动的,近三年来基本

android模拟器可以复制文件,如何将文件复制到Android模拟器以用于检测测试?_fatgn的博客-程序员宅基地

我正在尝试为Android派运行一个工具化的单元测试。我正在构建的测试将测试对通过Play Store APK扩展文件传递的扩展文件的访问。在手动测试中,这是可行的,但那是手动的。adb push obb/main.45.org.example.someapp.obb /storage/emulated/0/Android/obb/org.example.someapp/main.45.org.e...

随便推点

Android开发初级00_1有关Toast和Menu的知识点_想飞的孤独少年的博客-程序员宅基地

Toast和Menu的使用 Toast的使用.java文件<(直接在java文件中就可以使用)/b> /*按钮一的作用*///提示一个个点击事件 Button button1 = (Button) findViewById(R.id.button_1); button1.setOnClickListener(new View.OnClickListe

python typeerror总结_python异常总结解析_极相 空林玄一的博客-程序员宅基地

对python这个高级语言感兴趣的小伙伴,下面一起跟随编程之家 jb51.cc的小编两巴掌来看看吧!python用异常对象(exception object)来表示异常情况。遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的 回溯(Traceback, 一种错误信息)终止执行:&gt;&gt;&gt; 1/0Traceback (most recent call last):F...

JavaScript中实现数字保留2位小数_js 数字保留两位小数_疆~的博客-程序员宅基地

四舍五入:45.6784.toFixed(2) // 输出结果为 45.68不四舍五入Math.floor(45.6784 * 100) / 100 // 输出结果为 15.67

案例诊断:“交易耗时8S”缉凶记_大家叫我导演的博客-程序员宅基地

背景某日上午,小集购买a产品失败,页面弹出“系统异常,请稍后重试”的报错,便联系了技术团队的开发小成。“小成,我刚才尝试买a产品一直显示系统异常,是不是有什么问题呢?”开...

清淡闲雅之《浮生六记》_浮生六记 清淡_luoyayun361的博客-程序员宅基地

1无意中听到这本书的书名,是因为汪涵在节目上极力的推荐,后来在网上书店特意去搜索,光看介绍还觉得蛮不错,后来终于在某次的购物书单中添加了这本。2浮生六记,描述的是作者沈复生前写的六篇文章(如今仅存的只有四篇了),沈复,一个熟悉又陌生的名字,中学时候貌似学过他的文章,如今忘得一干二净。他生于乾隆二十八年(1763年),可以说是非常久远了,所以这本书毫无疑问,是一本文言文。说实话,第一次买...

winform 隐藏窗体_weixin_34233421的博客-程序员宅基地

转载于:流云拂雨的空间_百度空间 http://hi.baidu.com/lyfy_blog/item/e6ed8c02e57814c374cd3cc1 感谢原作者的分享。winform 隐藏窗体我们有的项目需要将窗体隐藏,只在通知栏显示一个小图标,在做隐藏窗体时,用this.visible=false 无效,经过查找资料终于解决,记录下法一、 this.ShowInTask...

推荐文章

热门文章

相关标签