12 分布式锁加入看门狗

1、看门狗的流程图

在这里插入图片描述

2、看门狗的代码实现
/****类说明:Redis的key-value结构*/
public class LockItem {private final String key;private final String value;public LockItem(String key, String value) {this.key = key;this.value = value;}public String getKey() {return key;}public String getValue() {return value;}
}
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;/****类说明:存放到延迟队列的元素,比标准的delay的实现要提前一点时间*/
public class ItemVo<T> implements Delayed{/*到期时刻  20:00:35,234*/private long activeTime;/*业务数据,泛型*/private T data;/*传入的数值代表过期的时长,单位毫秒,需要乘1000转换为毫秒和到期时间* 同时提前100毫秒续期,具体的时间可以自己决定*/public ItemVo(long expirationTime, T data) {super();this.activeTime = expirationTime+System.currentTimeMillis()-100;this.data = data;}public long getActiveTime() {return activeTime;}public T getData() {return data;}/*** 返回元素到激活时刻的剩余时长*/public long getDelay(TimeUnit unit) {long d = unit.convert(this.activeTime- System.currentTimeMillis(),unit);return d;}/**按剩余时长排序*/public int compareTo(Delayed o) {long d = (getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS));if (d==0){return 0;}else{if (d<0){return -1;}else{return  1;}}}}

核心代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** 分布式锁,附带看门狗线程的实现:加锁,保持锁1秒*/
@Component
public class RedisDistLockWithDog implements Lock {//加锁的时间private final static int LOCK_TIME = 1*1000;private final static String LOCK_TIME_STR = String.valueOf(LOCK_TIME);//key的开头,用于标记是分布式锁使用private final static String RS_DISTLOCK_NS = "tdln2:";//释放锁的lua,释放的时候保持原子性private final static String RELEASE_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('del', KEYS[1])\n" +"    else return 0 end";/*还有并发问题,考虑ThreadLocal*/private ThreadLocal<String> lockerId = new ThreadLocal<>();private Thread ownerThread;private String lockName = "lock";@Autowiredprivate JedisPool jedisPool;public String getLockName() {return lockName;}public void setLockName(String lockName) {this.lockName = lockName;}public Thread getOwnerThread() {return ownerThread;}public void setOwnerThread(Thread ownerThread) {this.ownerThread = ownerThread;}//加锁的入口@Overridepublic void lock() {//自璇while(!tryLock()){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException("不支持可中断获取锁!");}//具体的加锁逻辑@Overridepublic boolean tryLock() {Thread t=Thread.currentThread();/*说明本线程正在持有锁*/if(ownerThread==t) {return true;}else if(ownerThread!=null){/*说明本进程中有别的线程正在持有分布式锁*/return false;}Jedis jedis = null;try {jedis = jedisPool.getResource();/*每一个锁的持有人都分配一个唯一的id,也可采用snowflake算法*/String id = UUID.randomUUID().toString();SetParams params = new SetParams();params.px(LOCK_TIME); //加锁时间1sparams.nx();//synchronized是为了防止本地多个线程争抢synchronized (this){//加锁if ((ownerThread==null)&&"OK".equals(jedis.set(RS_DISTLOCK_NS+lockName,id,params))) {lockerId.set(id);setOwnerThread(t);if(expireThread == null){//看门狗线程启动expireThread = new Thread(new ExpireTask(),"expireThread");expireThread.setDaemon(true);//设置为守护线程expireThread.start();}//往延迟阻塞队列中加入元素(让看门口可以在过期之前一点点的时间去做锁的续期)delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockName,id)));System.out.println(Thread.currentThread().getName()+"已获得锁----");return true;}else{System.out.println(Thread.currentThread().getName()+"无法获得锁----");return false;}}} catch (Exception e) {throw new RuntimeException("分布式锁尝试加锁失败!",e);} finally {jedis.close();}}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException("不支持等待尝试获取锁!");}@Overridepublic void unlock() {if(ownerThread!=Thread.currentThread()) {throw new RuntimeException("试图释放无所有权的锁!");}Jedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockName),Arrays.asList(lockerId.get()));System.out.println(result);if(result.longValue()!=0L){System.out.println("Redis上的锁已释放!");}else{System.out.println("Redis上的锁释放失败!");}} catch (Exception e) {throw new RuntimeException("释放锁失败!",e);} finally {if(jedis!=null) jedis.close();lockerId.remove();setOwnerThread(null);}}@Overridepublic Condition newCondition() {throw new UnsupportedOperationException("不支持等待通知操作!");}/*看门狗线程*/private Thread expireThread;//通过delayDog 避免无谓的轮询,减少看门狗线程的轮序次数   阻塞延迟队列   刷1  没有刷2private static DelayQueue<ItemVo<LockItem>> delayDog = new DelayQueue<>();//续锁逻辑:判断是持有锁的线程才能续锁private final static String DELAY_LOCK_LUA ="if redis.call('get',KEYS[1])==ARGV[1] then\n" +"        return redis.call('pexpire', KEYS[1],ARGV[2])\n" +"    else return 0 end";private class ExpireTask implements Runnable{@Overridepublic void run() {System.out.println("看门狗线程已启动......");while(!Thread.currentThread().isInterrupted()) {try {LockItem lockItem = delayDog.take().getData();//只有元素快到期了才能take到  0.9sJedis jedis = null;try {jedis = jedisPool.getResource();Long result = (Long)jedis.eval(DELAY_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS+lockItem.getKey ()),Arrays.asList(lockItem.getValue(),LOCK_TIME_STR));if(result.longValue()==0L){System.out.println("Redis上的锁已释放,无需续期!");}else{delayDog.add(new ItemVo<>((int)LOCK_TIME,new LockItem(lockItem.getKey(),lockItem.getValue())));System.out.println("Redis上的锁已续期:"+LOCK_TIME);}} catch (Exception e) {throw new RuntimeException("锁续期失败!",e);} finally {if(jedis!=null) jedis.close();}} catch (InterruptedException e) {System.out.println("看门狗线程被中断");break;}}System.out.println("看门狗线程准备关闭......");}}//    @PostConstruct
//    public void initExpireThread(){
//
//    }@PreDestroypublic void closeExpireThread(){if(null!=expireThread){expireThread.interrupt();}}
}

测试:

@SpringBootTest
public class TestRedisDistLockWithDog {@Autowiredprivate RedisDistLockWithDog redisDistLockWithDog;private int count = 0;@Testpublic void testLockWithDog() throws InterruptedException {int clientCount =3;CountDownLatch countDownLatch = new CountDownLatch(clientCount);ExecutorService executorService = Executors.newFixedThreadPool(clientCount);for (int i = 0;i<clientCount;i++){executorService.execute(() -> {try {redisDistLockWithDog.lock(); //锁的有效时间1秒System.out.println(Thread.currentThread().getName()+"准备进行累加。");Thread.sleep(2000);count++;} catch (InterruptedException e) {e.printStackTrace();} finally {redisDistLockWithDog.unlock();}countDownLatch.countDown();});}countDownLatch.await();System.out.println(count);}@Testpublic void testTryLock2() {int clientCount =1000;for (int i = 0;i<clientCount;i++) {if (redisDistLockWithDog.tryLock()) {System.out.println("已获得锁!");redisDistLockWithDog.unlock();} else {System.out.println("未能获得锁!");}}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/154992.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消消乐游戏开发,消除类游戏

消除游戏是一类简单而又充满乐趣的休闲游戏&#xff0c;通过匹配相同的元素来完成任务&#xff0c;其简单直观的玩法吸引了大量玩家。本文将为你介绍设计和开发一款成功的消除游戏的关键步骤。 1. 确定核心玩法机制 消除游戏的核心在于匹配相同的元素。首先&#xff0c;明确定…

MyBatis Generator 插件 详解自动生成代码

MyBatis Generator&#xff08;MBG&#xff09;是MyBatis和iBATIS的代码生成器。可以生成简单CRUD操作的XML配置文件、Mapper文件(DAO接口)、实体类。实际开发中能够有效减少程序员的工作量&#xff0c;甚至不用程序员手动写sql。 它将为所有版本的MyBatis以及版本2.2.0之后的i…

YOLOV5 C++部署的人员检测项目【学习笔记(十一)】

本文为修改后的转载&#xff0c;没有转载链接&#xff0c;所以文章类型暂为原创 文章目录 一、安装Pytorch 及 YOLO v51.1 安装GPU版 pytorch1.2 安装YOLO v5所需依赖 二、YOLO v5训练自定义数据2.1 标注数据2.1.1 安装labelImg2.1.2 标注 2.2 准备数据集2.2.1 组织目录结构2.…

深度学习之三(卷积神经网络--Convolutional Neural Networks,CNNs)

概念 卷积神经网络(Convolutional Neural Networks,CNNs)是一种特殊的神经网络结构,专门用于处理具有网格状结构(如图像、音频)的数据。CNN 在计算机视觉领域取得了巨大成功,广泛应用于图像识别、物体检测、图像生成等任务。以下是 CNN 的主要理论概念: 在数学中,卷…

TeXLive 2023安装教程

TeXLive 2023安装教程 本文介绍最新TeX发行版——TeXLive 2023的安装步骤。如果你想用LaTeX进行写作&#xff0c;那么需要搭建LaTeX环境&#xff1a;可以选择下面两种方案之一进行安装&#xff1a;(1)TeXLive 2023TeXStudio或者(2)TeXLive 2023WinEdt 11。其中TeXLive 2023是由…

[Mac软件]Downie 4.6.34视频下载工具

以下是关于Downie软件的介绍&#xff1a; Downie是一款非常实用的视频下载软件&#xff0c;专门为Mac用户设计。这款软件的使用方法非常简单&#xff0c;只需要将想要下载的视频链接复制到Downie的界面&#xff0c;它就能够自动下载。 Downie最大的特点就是支持的网站非常多&a…

五大匹配算法

五大匹配算法 五大匹配算法 BF 匹配、RK 匹配、KMP 匹配、BM 匹配、Sunday 匹配。 1、BF 匹配 // BF 匹配&#xff08;暴力匹配&#xff09; public static int bfMatch(String text, String pattern) {char[] t text.toCharArray();char[] p pattern.toCharArray();int i …

vs code git问题:文件明明已加入忽略文件中,还是出现

vs code git问题&#xff1a;文件明明已加入忽略文件中&#xff0c;还是出现 原因&#xff1a; 因为之前这些文件都已经提交过&#xff0c;线上GIT已经存在&#xff0c;已存在就不能忽略&#xff0c; 解决办法&#xff1a; 先要删除这些文件提交上去&#xff0c;然后把这些文…

使用USB转JTAG芯片CH347在Vivado下调试

简介 高速USB转接芯片CH347是一款集成480Mbps高速USB接口、JTAG接口、SPI接口、I2C接口、异步UART串口、GPIO接口等多种硬件接口的转换芯片。 通过XVC协议&#xff0c;将CH347应用于Vivado下&#xff0c;简单尝试可以成功&#xff0c;源码如下&#xff0c;希望可以一起共建&a…

软件系统运维方案

1.项目情况 2.服务简述 2.1服务内容 2.2服务方式 2.3服务要求 2.4服务流程 2.5工作流程 2.6业务关系 2.7培训 3.资源提供 3.1项目组成员 3.2服务保障 点击获取所有软件开发资料&#xff1a;点我获取

一文了解Spring依赖注入时循环依赖问题

目录 什么是循环依赖 凡是Java的循环依赖都会有问题&#xff1f; 为什么Spring循环依赖会有问题&#xff1f; Spring解决循环依赖问题的思路&#xff1f; 设置二级缓存对象池 方案一直接将实例化对象放入早期对象池 方案一缺点 方案二-将实例化对象处理AOP后放入早期对象…

入行IC | 从小白助理级,到总监专家级,到底要经历怎样的成长阶段呢?

《中国集成电路产业人才发展报告》是业内和IC设计、IC人才都息息相关的一份报告。 &#xff08;文末可领全部报告资料&#xff09; * 从报告数据来看&#xff0c;无论在半导体产业的哪个环节&#xff0c;个人发展路径和年薪待遇都是逐级攀升的趋势。 那么从小白助理级&a…

口袋参谋:找关键词的三种方法!

​如何找热搜关键词&#xff1f;99%的商家都不知道。那么今天可以根据我说的三种方法去做。 第一种方法&#xff1a;利用竞争对手 通过分析竞争对手&#xff0c;正在使用和采取何种优化方法&#xff0c;来帮助你理解市场上正在流行什么样的关键字&#xff0c;这些热词可以直接从…

Java自动化驱动浏览器搜索稻香

下载最新的Chrome浏览器 查看chrome版本&#xff0c;在浏览器地址栏输入&#xff1a;chrome://version/ 下载对应的浏览器驱动&#xff0c;将其放到一个目录中&#xff0c;我放到了D:/chromedriver-win64 导入对应的依赖【注意&#xff1a;不要导入最新的版本&#xff0c;最…

数字化转型导师坚鹏:数字化时代银行网点厅堂营销5大重点分析

数字化时代银行网点厅堂营销存在以下5大重点&#xff1a; 1、厅堂宣传。应以主推产品作为厅堂宣传的宣传重点&#xff0c;结合视频宣传、平面物料、互动机具、陈列物料等多维度&#xff0c;开展有序重复展示&#xff0c;进而加大吸引客户关注度。 2、产品推荐。在识别出中高端…

纽扣电池/含纽扣电池产品上架亚马逊各国法规标准要求16 CFR 第 1700.15/20 ANSI C18.3M(瑞西法案认证)

亚马逊纽扣电池认证标准有哪些&#xff1f; 一、美国站&#xff08;亚马逊纽扣电池/含纽扣电池商品&#xff09;安全测试标准要求&#xff1a; 16 CFR 第 1700.15 、16 CFR 第 1700.20 ANSI C18.3M、警示标签声明要求&#xff08;第 117-171 号公众法&#xff09; 二、澳大…

Linux应用开发基础知识——网络通信编程(九)

前言&#xff1a; 通过学习为后续Linux网络编程奠定基础。首先介绍网络编程的概念&#xff0c;即网络协议分层&#xff0c;旨在帮助读者对网络建立初步的、全面立体的认识&#xff0c;其次介绍包括协议、端口、地址等&#xff1b;最后介绍应用非常广泛的传输控制协议&#xff0…

CentOS8部署Skywalking(非容器方式)

一、官网下载安装包 二、安装 #tar -zxf apache-skywalking-apm-9.6.0.tar.gz #mv apache-skywalking-apm-9.6.0 skywalking #cd /opt/skywalking 修改配置文件 #vi /opt/skywalking/config/application.yml #vi vi /opt/skywalking/webapp/application.yml 三、运行 ./bin…

NLP | SimKGC论文详解及项目实现

本文主要讲解了论文SimKGC&#xff1a;基于预训练语言模型的简单对比知识图谱补全的论文总结以及项目实现。 论文题目&#xff1a;2022_SimKGC: Simple Contrastive Knowledge Graph Completion with Pre-trained Language Models 论文地址&#xff1a;2022.acl-long.295.pdf (…

竞赛 题目:基于深度学习的图像风格迁移 - [ 卷积神经网络 机器视觉 ]

文章目录 0 简介1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习卷积神经网络的花卉识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…