文章摘自:https://www.cnblogs.com/0201zcr/p/5942748.html

package com.abtc.server.mine.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.UUID; public class RedisLock
{
private static Logger logger = LoggerFactory.getLogger(RedisLock.class); private RedisTemplate redisTemplate; private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 10; // 记录当前获取锁的标识
private String uuid = UUID.randomUUID().toString(); /**
* Lock key path.
*/
private String lockKey; /**
* 锁超时时间,防止线程在入锁以后,无限的执行等待
*/
private int expireMsecs = 60 * 1000; /**
* 锁等待时间,防止线程饥饿
*/
private int timeoutMsecs = 10 * 1000; private volatile boolean locked = false; /**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
*
* @param lockKey lock key (ex. account:1, ...)
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey)
{
this.redisTemplate = redisTemplate;
this.lockKey = lockKey + "_lock";
this.uuid = uuid;
} /**
* Detailed constructor with default lock expiration of 60000 msecs.
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs)
{
this(redisTemplate, lockKey);
this.timeoutMsecs = timeoutMsecs;
} /**
* Detailed constructor.
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs)
{
this(redisTemplate, lockKey, timeoutMsecs);
this.expireMsecs = expireMsecs;
} /**
* @return lock key
*/
public String getLockKey()
{
return lockKey;
} private String get(final String key)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.get(serializer.serialize(key));
connection.close();
if (data == null)
{
return null;
}
return serializer.deserialize(data);
}
});
}
catch (Exception e)
{
logger.error("get redis error, key : {}", key);
}
return obj != null ? obj.toString() : null;
} private boolean setNX(final String key, final String value)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
connection.close();
return success;
}
});
}
catch (Exception e)
{
logger.error("setNX redis error, key : {}", key);
}
return obj != null ? (Boolean) obj : false;
} private String getSet(final String key, final String value)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
connection.close();
return serializer.deserialize(ret);
}
});
}
catch (Exception e)
{
logger.error("setNX redis error, key : {}", key);
}
return obj != null ? (String) obj : null;
} /**
* 获得 lock.
* 实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
* reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
* 执行过程:
* 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
* 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
*
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public synchronized boolean lock() throws InterruptedException
{
int timeout = timeoutMsecs;
while (timeout >= 0)
{
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires) + "&" + uuid; //锁到期时间 + uuid
if (this.setNX(lockKey, expiresStr))
{
// lock acquired
locked = true;
return true;
}
String currentValueStr = this.get(lockKey); //redis里的时间
if (currentValueStr != null && !"".equals(currentValueStr))
{
String[] split = currentValueStr.split("&");
if (split[0] != null && Long.parseLong(split[0]) < System.currentTimeMillis())
{
// 判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
String oldValueStr = this.getSet(lockKey, expiresStr);
// 获取上一个锁到期时间,并设置现在的锁到期时间,
// 只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr))
{
// 防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受 // [分布式的情况下]:如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
locked = true;
return true;
}
}
}
Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
}
return false;
} /**
* Acqurired lock release.
*/
public synchronized void unlock()
{
if (locked)
{
String keyValue = this.get(lockKey);
if (keyValue != null && !"".equals(keyValue))
{
String[] split = keyValue.split("&");
// 判断uuid是否已经被修改
if (split.length == 2 && uuid.equals(split[1]))
{
redisTemplate.delete(lockKey);
}
}
locked = false;
}
} }

  

调用:

RedisLock lock = null;
try {
//先获取状态,如果为Y则任务已经发送完毕
if("Y".equals(redisutil.getString("flg"))){
return;
}
lock = new RedisLock(redisTemplate, "flg",10000,20000);//这里的flg最终会变成flg_lock
if(lock.lock()){
//再次检索,因为有可能开始获取状态不是Y,但是在获取锁的过程中被别人置成Y,所以再次检索确保不重发
if("Y".equals(redisutil.getString("flg"))){
return;
}
redisutil.set("flg", "Y",300);
    //======================================start
    //执行自己需要的任务
    //======================================end
  }
} catch (Exception e) {
e.printStackTrace();
}finally {
  if(lock != null)
  {lock.unlock();}
}

  redisutil网上应该有很多,应该能搜到

注意:虽然可以实现我想要的效果,但是我个人有个疑问,分布式系统的时间是怎样统一的,如果时间不一样,可能会出问题

分布式系统定时任务,保证只有一个服务执行了改任务--采用redis分布式锁来实现(文章摘自:https://www.cnblogs.com/0201zcr/p/5942748.html)的更多相关文章

  1. Redis 分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!(转)

    基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本篇文章主要是基于我们实际项目中因为redis分布式锁造成的事故分析及解决方案.我们项目中的抢购订单采用的是分布式锁来解决的,有一次,运营做了一 ...

  2. SpringCloud微服务实战——搭建企业级开发框架(三十九):使用Redis分布式锁(Redisson)+自定义注解+AOP实现微服务重复请求控制

      通常我们可以在前端通过防抖和节流来解决短时间内请求重复提交的问题,如果因网络问题.Nginx重试机制.微服务Feign重试机制或者用户故意绕过前端防抖和节流设置,直接频繁发起请求,都会导致系统防重 ...

  3. 利用redis 分布式锁 解决集群环境下多次定时任务执行

    定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...

  4. Redis分布式锁服务(八)

    阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...

  5. Redis分布式锁服务(转)

    原文:http://www.cnblogs.com/mushroom/p/4752499.html 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj ...

  6. Redis分布式锁服务

    阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...

  7. 干货,不小心执行了rm -f,除了跑路,如何恢复?https://www.cnblogs.com/justmine/p/10359186.html

    前言 每当我们在生产环境服务器上执行rm命令时,总是提心吊胆的,因为一不小心执行了误删,然后就要准备跑路了,毕竟人不是机器,更何况机器也有bug,呵呵. 那么如果真的删除了不该删除的文件,比如数据库. ...

  8. 你懂什么是分布式系统吗?Redis分布式锁都不会?

    分布式系统涉及到很多的技术.理论与协议,很多人也说,分布式系统是“入门容易,深入难”,有一些人简历上写着熟悉分布式系统,很多人都是管中窥豹只见一斑. 究竟什么是分布式系统? 分布式系统是由一组通过网络 ...

  9. thinkphp5使用workerman的定时器定时任务在某一个时间执行

    1.首先通过 composer 安装workerman,在thinkphp5完全开发手册的扩展->coposer包->workerman有详细说明: #在项目根目录执行以下指令compos ...

随机推荐

  1. Spring Cloud配置中心(Config)

    Spring Cloud配置中心(Config) Spring Cloud是现在流行的分布式服务框架,它提供了很多有用的组件.比如:配置中心.Eureka服务发现. 消息总线.熔断机制等. 配置中心在 ...

  2. fabric 更详尽的用法

    项目发布和运维的工作相当机械,频率还蛮高,导致时间浪费在敲大量重复的命令上. 修复bug什么的,测试,提交版本库(2分钟),ssh到测试环境pull部署(2分钟),rsync到线上机器A,B,C,D, ...

  3. 使用curl自动签到百度贴吧

    百度贴吧的一键签到接口地址为 http://tieba.baidu.com/tbmall/onekeySignin1 curl http://tieba.baidu.com/tbmall/onekey ...

  4. UI自动化(三)css优先级

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. bzoj 4358 Permu - 莫队算法 - 链表

    题目传送门 需要高级权限的传送门 题目大意 给定一个全排列,询问一个区间内的值域连续的一段的长度的最大值. 考虑使用莫队算法. 每次插入一个数$x$,对值域的影响可以分成4种情况: $x - 1$, ...

  6. CEF 右键添加开发者选项菜单项

    在项目开发过程中,有时候需要进行调试测试,然后我们可以在cef上下文菜单中添加自定义开发者工具菜单项,这样会比较方便,最后效果: 实现过程: 让自己的MyClientHandler来继承 CefCon ...

  7. centos6.5下安装tomcat

    linux在安装tomcat之前必须已安装jdk 已下载好tomcat 拖到centos系统的桌面 现在在桌面目录下 mv apache-tomcat-8.5.39.tar.gz /usr/local ...

  8. IOS面试题2018/11/17

    1.设计模式是什么?你知道哪些设计模式? 设计模式是一种编码经验,就是一种成熟的逻辑去处理某一种类型的事情. 1.MVC模式:model view controller,把模型,视图,控制器 层进行解 ...

  9. pt站 扫盲贴 面向小白

    1.什么是pt站? 大家应该知道种子,平时下电影的渠道除了百度云,就是种子了.这种普通的的种子称为bt. pt是私人种,和bt的区别是:不是谁想下就能下的,你得加入一个社区,入了伙,社区成员之间才能相 ...

  10. Web开发中button与submit区别

    submit是button的一个特例,也是button的一种,它把提交这个动作自动集成了. 如果表单在点击提交按钮后需要用JS进行处理(包括输入验证)后再提交的话,通常都必须把submit改成butt ...