前言:需求是这样的,在与第三方对接过程中,对方提供了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 如何优雅的实现时间控制相关推荐

  1. Java如何优雅的实现时间控制

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | http://rrd.me/gCQHp 前言:需求是这样 ...

  2. redistemplate hash 过期时间_完美!谈谈Java中时间控制的几种解决方案

    前言 需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可 ...

  3. 怎样用Java 8优雅的开发业务

    怎样用Java 8优雅的开发业务 文章目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类`CompletableFuture` 单机批处理多线程 ...

  4. Java编程那些事儿78——时间和日期处理

    Java编程那些事儿78--时间和日期处理 陈跃峰 出自:http://blog.csdn.net/mailbomb 9.6 java.util包 java.util包是Java语言提供的工具类包,该 ...

  5. 你的Kubernetes Java应用优雅停机了吗?

    假如我们从 kafka 拉取数据然后生成任务处理数据,在服务退出时,如何保证内存中的数据能被正常处理完不丢失呢?假如服务是部署在 Kubernetes 中又该如何处理? Java 应用优雅停机 我们首 ...

  6. JAVA游戏开发之FPS精准控制

    目录 JAVA游戏开发之FPS精准控制... 1 1       概述... 2 1.1        编写目的... 3 2       FPS精准控制... 3 2.1        FPS描述. ...

  7. 我们一起来排序——使用Java语言优雅地实现常用排序算法

    破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...

  8. JAVA中获取当前系统时间

    JAVA中获取当前系统时间 转自:http://www.cnblogs.com/Matrix54/archive/2012/05/01/2478158.html 一. 获取当前系统时间和日期并格式化输 ...

  9. C++ 多线程:时间控制

    C++多线程库中的各个子库都有各自的时间控制方式,依此来进行多线程程序运行中cpu资源的精确控制. 使用std::chrono时间库可以提供微妙.毫秒.秒及以上的时间取用,并且能够获取当前系统时间. ...

最新文章

  1. Android 打包流程
  2. 计算机专业建设思路和措施,计算机网络专业教学改革与建设思路措施
  3. 原始套接字SOCK_RAW
  4. Pandas常用I/O(一)------read_csv(),read_table()
  5. 第10步 (1)logback.xml日志配置(2) ftp(上传文件)服务器配置(3) idea注入和自动编译配置(4)项目提交gitee(5)fe助手和restlet client
  6. 简洁jQuery滑动门插件
  7. redis获取存在的键值_Redis 分布式锁、限流
  8. Linux下的压缩和解压缩命令——compress/uncompress
  9. 日语五十音平假名 识读卡片 笔顺
  10. 西门子PLC与安川变频器Modbus通信
  11. redis-trib.rb找不到
  12. 微信直接下载app的解决方案
  13. opencv 阈值处理(python)
  14. 群晖nas不能修改php.ini,群晖nas 修改nginx配置的问题
  15. docker如何使用阿里加速器的方法
  16. 网站服务器防御怎么查,如何查看服务器被攻击
  17. 国产游戏面临新一轮洗牌?虚幻4引擎免费开源
  18. java中的抽象到底是个啥?
  19. iframe相关,iframe样式
  20. win10 网页 你尚未连接 代理服务器可能有问题,或地址不正确。

热门文章

  1. 有趣、好玩、有料的网站收藏
  2. 《大话数据结构》第9章 排序 9.8 归并排序(上)
  3. java.lang.RuntimeException: Unable to start activity ComponentInfo
  4. 问题分享:Js引用类型赋值
  5. linux 使用spinlock的配对关系问题
  6. Zabbix+shell监控报警任意web
  7. 缓冲区 cin() getline() getchar()
  8. hibernate中持久化对象的生命周期(三态:自由态,持久态,游离态 之间的转换)...
  9. 配置SQL Server 2005 远程连接(转)
  10. Cisco BFD双向转发检测技术部署案例