专栏首页代码人生分布式锁的实现以及在定时器中的应用

分布式锁的实现以及在定时器中的应用

分布式锁是为了保证分布式各系统对于资源的强占,独占。分布式锁的设计与多线程锁设计一样,都是通过一个信号量,对它进行CAS(compare and set)原子操作来实现乐观锁,或通过一个独占锁实现悲观锁,悲观锁不推荐。

乐观锁

乐观锁的核心是通过信号量代表资源,通过CAS的操作去标志改信号被占用。CAS成功,代表资源没有被占用,执行任务;CAS失败,代表资源被占用或处理过,不执行改资源。

失败后循环CAS的操作就叫做无锁自旋。JUC源码中,锁的实现,就是通过safe进行无锁自旋。

分布式锁的应用 - 定时任务

分布式锁在定时任务时会被使用到。分布式服务上,每个服务都有定时任务,如何保证定时任务执行的资源只执行一次,可以用分布式锁来锁住资源实现。也可以使用hash资源定位服务来实现。

定时任务分布式锁按锁的粒度,有两种思路实现。一种是定时任务加锁,即一个定时任务,只可在一个服务上执行;另一种是定时任务的每个资源加锁,即定时任务在所有服务上都执行,但是每个资源自会在一个服务节点执行。

定时任务,定时任务加锁

给定时任务加上一个信号量,定时任务执行时,CAS一下,如果信号加上去,就代表没有其他节点执行定时任务,就执行;如果CAS失败,就代表已经执行了,就不要再执行这次任务了。

下面一种通过数据库来实现 ,我们加上一个定时任务表,字段有执行时间,version字段,每个定时任务对应表中的一条记录,通过update ... where version = and update_date=做CAS操作。分布式系统CAS操作失败,代表该定时任务已被其他节点执行,它就不用执行了。

CREATE TABLE `job_mutex` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `job_type` tinyint(3) NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `version` int(12) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
TaskJobSchedu taskJobSchedu = taskJobScheduMapper.queryTaskJobScheduWait(jobType);
//update job_mutex set version=#{new_veriosn}, update_time=#{now} where version=#{verison} and TO_DAYS(update_time) = TO_DAYS(#{now})
//定时任务一天执行一次
if(taskJobScheduMapper.updateVersonToday(taskJobSchedu, taskJobSchedu.getVersion()+1, new Date())) {
  execute();
}

当当的开源项目Elastic-job就有如此实现

定时任务,按每个任务加锁

如果想将分布式锁的粒度放在每个资源上,即定时任务在每个节点服务上都执行,但是它们执行的资源不会重复。这个可以通过双写来解决。执行前先通过CAS写资源到一个中间状态,执行成功结束后,将资源写到完成状态。加上一个过期设置,如中间状态15分钟后,就认为资源执行失败,回滚重新执行。

这里是一种通过redis和DB双写来实现资源的,这里通过redisTemplate的setIsAbsent来做原子操作,db中资源那张表,加个字段表示是否执行定时任务。

如果执行任务失败,它就不会写到DB,在redis中的key超时后的定时任务会再次执行这个任务。

List<Task> tasks = getNotExecuteTaskInDB();
for(Task task: tasks) {
	ValueOperations<String, String> tasksRedis = redisTemplate.opsForValue();
	if (tasksRedis.setIfAbsent(keyPrev+task.id, "1", aliveTime, TimeUnit.MINUTES) {
		execute();
		setTaskToExecuted(task);
	}
}

悲观锁

悲观锁亦可实现,但不推荐,悲观锁使用数据库可以用select for update来独占资源。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • socket系列(三)——Spring-socket实时通信、推送

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    逝兮诚
  • JAVA8新特性(一)——函数性编程

    函数式编程可以使java能使用Lambda表达式进行函数式编程。函数式编程实际上是实现接口的匿名函数。

    逝兮诚
  • Spring事务失效的两种情况

    spring的事务默认是对RuntimeException进行回滚,而不继承RuntimeException的不回滚。因为在java的设计中,它认为不继承Run...

    逝兮诚
  • Android Jetpack架构组件(七)之WorkManager

    在Android应用开发中,或多或少的会有后台任务的需求,根据需求场景的不同,Android为后台任务提供了多种不同的解决方案,如Service、Loader、...

    xiangzhihong
  • 如何长时间高效学习?

    熟悉我的人,知道我同时可以干很多事情。所以,经常会有人问我,有什么好的习惯、高效的学习方法,可以提高效率?

    猴子数据分析
  • Java基础-流程控制(顺序结构)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明...

    cwl_java
  • iOS 9 Storyboard 教程(二下)

    现在你会忽视Game行,仅仅让用户输入玩家的名字. 当用户点击Cancel按钮的时候,这个控制器将会关闭并且不管你输了什么数据都不会保存.这个部分用unwin...

    hrscy
  • Facebook关闭旗下VR电影工作室,将目标投向对外合作VR电影创作

    镁客网
  • 走进科学:现代汽车的大脑与安全

    网络安全或者渗透测试,对于我等还在路上的人来说,路漫漫。要学习的东西太多,扎实的专业知识不说,还有社会工程学,以及心思缜密的推理分析等等,不亚于一个侦探。这不,...

    FB客服
  • 10个最受欢迎的 JavaScript 框架,以及它们的主要特征和功能

    多年来,业界已经发布了大量 JavaScript 框架,怎样进行选择可能是一个挑战。如果你感到困惑,不知道应该选哪个或者究竟哪个适合你,那么我已经帮你解决了问题...

    用户5827212

扫码关注云+社区

领取腾讯云代金券