Java 如何优雅的实现时间控制
前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
一:时间控制的几种方案
1.1: 从线程方面解决
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
1.2:使用Timer
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
public class TimmerTest {/*** 测试方法*/public void test() {Timer timer = new Timer();timer.schedule(new MyTask(), 800);}public class MyTask extends TimerTask {/*** 运行方法*/@Overridepublic void run() {System.out.println("输出");}}
}
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
1.3:redis延时
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
二:redis
2.1:maven中引入redis
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
2.2: 在springboot中配置redis
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Autowiredprivate RedisTemplate redisTemplate;/*** redisTemplate实例化** @return*/@Beanpublic RedisTemplate redisTemplateInit() {//设置序列化Key的实例化对象redisTemplate.setKeySerializer(new StringRedisSerializer());//设置序列化Value的实例化对象redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}}
2.2:redisTemplate模板工具类
@Component
public class RedisManager {private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);@Autowiredprivate RedisTemplate redisTemplate;/*** 设置对象** @param key key* @param value value值* @param <T> 返回值泛型* @return 正确的值:<T> 错误的值:null*/@SuppressWarnings("unchecked")public <T> ValueOperations<String, T> setObject(final String key, final T value) {final ValueOperations<String, T> operation = redisTemplate.opsForValue();operation.set(key, value);return operation;}/*** 设置对象及失效时间 (单位:秒)** @param key key* @param value value值* @param <T> 返回值泛型* @param time 秒值* @return 正确的值:<T> 错误的值:null*/@SuppressWarnings("unchecked")public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {final ValueOperations<String, T> operation = redisTemplate.opsForValue();operation.set(key, value, time, TimeUnit.SECONDS);return operation;}/*** 设置对象及失效时间(单位:毫秒)** @param key key* @param value value值* @param <T> 返回值泛型* @param time 秒值* @return 正确的值:<T> 错误的值:null*/@SuppressWarnings("unchecked")public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {final ValueOperations<String, T> operation = redisTemplate.opsForValue();operation.set(key, value, time, TimeUnit.MILLISECONDS);return operation;}/*** 获取对象** @param key 键* @return 正确的值:Object值对象<br>* 错误的值:null*/@SuppressWarnings("unchecked")public Object getObject(final String key) {final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();if (valueOperations == null || !redisTemplate.hasKey(key)) {return null;}final Object object = valueOperations.get(key);return object;}/*** 从缓存中获取string值** @param key* @return*/@SuppressWarnings("unchecked")public String getString(final String key) {String value = "";final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();if (valueOperations != null && redisTemplate.hasKey(key)) {final Object object = valueOperations.get(key);if (null != object) {LOGGER.info("--getString--object not empty");value = object.toString();} else {LOGGER.info("--getString--object empty");}}return value;}
2.2:在redis中实现时间控制
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
import com.youjia.orders.redis.RedisManager;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Objects;/*** @Auther: Yrion* @Date: 2019-01-11 23:36*/public class RedisTest extends OrderProviderApplicationTests {@Autowiredprivate RedisManager redisManager;@Testpublic void test() {controlTime("10000001", 10L);}public void controlTime(String requestId, Long timeOut) {if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {return;}//something codefinal String value = "value";redisManager.setObject(requestId, value, timeOut);final long startTime = System.currentTimeMillis();System.out.println("开始控制时间");//startfor (; ; ) {if (Objects.isNull(redisManager.getObject(requestId))) {break;}}final long endTime = System.currentTimeMillis();final long useTime = endTime - startTime;System.out.println("一共耗费时间:" + useTime);}
}
outPut:
开始控制时间
一共耗费时间:10042
三:总结
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
Java 如何优雅的实现时间控制相关推荐
- Java如何优雅的实现时间控制
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | http://rrd.me/gCQHp 前言:需求是这样 ...
- redistemplate hash 过期时间_完美!谈谈Java中时间控制的几种解决方案
前言 需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可 ...
- 怎样用Java 8优雅的开发业务
怎样用Java 8优雅的开发业务 文章目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类`CompletableFuture` 单机批处理多线程 ...
- Java编程那些事儿78——时间和日期处理
Java编程那些事儿78--时间和日期处理 陈跃峰 出自:http://blog.csdn.net/mailbomb 9.6 java.util包 java.util包是Java语言提供的工具类包,该 ...
- 你的Kubernetes Java应用优雅停机了吗?
假如我们从 kafka 拉取数据然后生成任务处理数据,在服务退出时,如何保证内存中的数据能被正常处理完不丢失呢?假如服务是部署在 Kubernetes 中又该如何处理? Java 应用优雅停机 我们首 ...
- JAVA游戏开发之FPS精准控制
目录 JAVA游戏开发之FPS精准控制... 1 1 概述... 2 1.1 编写目的... 3 2 FPS精准控制... 3 2.1 FPS描述. ...
- 我们一起来排序——使用Java语言优雅地实现常用排序算法
破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...
- JAVA中获取当前系统时间
JAVA中获取当前系统时间 转自:http://www.cnblogs.com/Matrix54/archive/2012/05/01/2478158.html 一. 获取当前系统时间和日期并格式化输 ...
- C++ 多线程:时间控制
C++多线程库中的各个子库都有各自的时间控制方式,依此来进行多线程程序运行中cpu资源的精确控制. 使用std::chrono时间库可以提供微妙.毫秒.秒及以上的时间取用,并且能够获取当前系统时间. ...
最新文章
- Android 打包流程
- 计算机专业建设思路和措施,计算机网络专业教学改革与建设思路措施
- 原始套接字SOCK_RAW
- Pandas常用I/O(一)------read_csv(),read_table()
- 第10步 (1)logback.xml日志配置(2) ftp(上传文件)服务器配置(3) idea注入和自动编译配置(4)项目提交gitee(5)fe助手和restlet client
- 简洁jQuery滑动门插件
- redis获取存在的键值_Redis 分布式锁、限流
- Linux下的压缩和解压缩命令——compress/uncompress
- 日语五十音平假名 识读卡片 笔顺
- 西门子PLC与安川变频器Modbus通信
- redis-trib.rb找不到
- 微信直接下载app的解决方案
- opencv 阈值处理(python)
- 群晖nas不能修改php.ini,群晖nas 修改nginx配置的问题
- docker如何使用阿里加速器的方法
- 网站服务器防御怎么查,如何查看服务器被攻击
- 国产游戏面临新一轮洗牌?虚幻4引擎免费开源
- java中的抽象到底是个啥?
- iframe相关,iframe样式
- win10 网页 你尚未连接 代理服务器可能有问题,或地址不正确。
热门文章
- 有趣、好玩、有料的网站收藏
- 《大话数据结构》第9章 排序 9.8 归并排序(上)
- java.lang.RuntimeException: Unable to start activity ComponentInfo
- 问题分享:Js引用类型赋值
- linux 使用spinlock的配对关系问题
- Zabbix+shell监控报警任意web
- 缓冲区 cin() getline() getchar()
- hibernate中持久化对象的生命周期(三态:自由态,持久态,游离态 之间的转换)...
- 配置SQL Server 2005 远程连接(转)
- Cisco BFD双向转发检测技术部署案例