文章摘自: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. Linux Shell自动交互/人机交互的三种方法

    如果你要学习linux,你可能会遇到Linux Shell自动交互问题,这里将介绍Linux Shell自动交互的解决方法,在这里拿出来和大家分享一下. 一.背景 shell脚本在处理自动循环或大的任 ...

  2. Lua 求当前月份的最大天数

    [1]实现代码 -- 第一种方式:简写 , day = })) print('The first way result : dayAmount = ' .. dayAmount) -- 第二种方式:分 ...

  3. K8S学习笔记之Kubernetes 部署策略详解

    0x00 概述 在Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了. 选择正确的部署策略是要依赖于我们的业务需求的,下面我们 ...

  4. java与javax的区别分析(转)

    Java是一种受C语言影响的编程语言.Java和Javax本质上是与Java编程语言的上下文一起使用的包.实际上Java和Javax没有区别.这只是不同的名字. Java是一种编程语言,受到C语言的影 ...

  5. FL studio钢琴卷工具简介

    FL studio中的钢琴卷工具在业内各编曲软件中享有当之无愧的声誉.钢琴卷是一款将音符和自动数据发送到钢琴卷的频道相关联的插件.而钢琴卷的这个操作运行过程被称为“排序”. 下面给大家介绍讲解钢琴卷中 ...

  6. 【2.0】SpringBoot连接MySql 8.0的url设置

    jdbc:mysql://localhost:3306/enterprise?useUnicode=true&amp&useSSL=false&amp&characte ...

  7. jQuery validator plugin之Selector

    原文 :unchecked Selector Selects all elements that are unchecked. jQuery( ":unchecked" ) Inv ...

  8. 自制URL转换器

    自定义 url 转换器五个步骤: 定义一个类. 在类中定义一个属性  regex  ,这个属性是用来保存 url 转换器规则的正则表达式. 实现  to_python(self,value)  方法, ...

  9. Java内存可见性

    如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的.如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量.Java内存模型(Java ...

  10. Pandas 基础(12) - Stack 和 Unstack

    这节的主题是 stack 和 unstack, 我目前还不知道专业领域是怎么翻译的, 我自己理解的意思就是"组成堆"和"解除堆". 其实, 也是对数据格式的一种 ...