数学建模社区-数学中国
标题:
Java如何优雅的实现时间控制
[打印本页]
作者:
杨利霞
时间:
2020-5-3 16:03
标题:
Java如何优雅的实现时间控制
2 `! @- r( ~! }& \2 a) ]) G
Java如何优雅的实现时间控制
前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
0 H$ H* y* X2 `* `+ f% e4 S6 L! x
! U5 C y4 u) P" H8 h4 k- \' T3 u9 j
一:时间控制的几种方案
: r8 P- M+ d" A9 W0 l, }
* b# q& J) @& X/ N8 L
1.1: 从线程方面解决
1 p+ k% t* V& e$ P! z- @
! v- @" @- }" u) D3 F/ B6 M
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
! }* N( z+ q8 z/ \; L( Z. U* U( B
. n# o% p, R. N- g3 o
1.2:使用Timer
8 \+ J& C3 f) ?7 n
2 y) t \. G8 y- f& g/ W( A
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
6 l% b. u# x9 u' W
9 E2 ^5 |4 |; r
public class TimmerTest {
# s, @/ k6 C% d- h( L, T
/**
* d9 D- z1 e: h
* 测试方法
* t2 D7 i+ t u
*/
& B' g. ?2 ]7 m6 w( X
public void test() {
6 T' h2 L8 |" K! v
Timer timer = new Timer();
: \7 U) Y5 }- i4 v- u- z& S+ |4 n' o0 R
timer.schedule(new MyTask(), 800);
& u; r, F1 {# [* w
}
4 `5 `* T5 x/ _: X. @+ c1 c( j- j
( @/ J5 D) z* \3 k0 [# l3 p
public class MyTask extends TimerTask {
2 E/ p) x o0 a. X
4 f# P# b* j( W4 Z) U$ _
/**
! s7 Q: {4 f6 c+ H3 R
* 运行方法
& l8 g) S, N9 O6 y) Z. A, q! C# _
*/
4 D/ J! a: \' d* ~: N$ o! `: s" P
@Override
$ T$ f* T0 U' t" l* R
public void run() {
5 c0 `* ~/ g, s8 j+ o- D; \$ }. U8 h
System.out.println("输出");
2 z0 o' r! e* F) U/ N+ J
}
1 X0 P( X4 {1 X
}
% M& H- \/ f4 _- f u( f
}
/ O8 K0 T# e! ?' {7 G' g
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
6 B% A$ k: _9 R0 }1 B+ ?
( D/ j9 E+ G/ a" u( U/ e
1.3:redis延时
2 o3 r- D1 \, k
& p& F1 r7 g+ x0 [2 R
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
: K/ Z# p2 w `% }1 J9 _
& U% \0 Q2 |8 w2 }3 ~6 Q5 A
8 q( }' H6 [; V' h
- L/ J; Q/ v8 Y2 ^( n$ _
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
5 O/ w. z! V) w7 s4 Q9 |
3 R, m7 s# T0 o/ H+ D) U' Z
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
1 D* Q2 N9 `, ?+ Z. V6 W
1 I+ z5 H5 W6 Z- A3 b+ H
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
4 f* a1 O" @- W$ F' _
- e# ~) z1 P# }
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
8 [! ^1 Y5 [! w2 i1 ~
! P( c2 Z) G4 T# F! Z/ E/ n! N% i5 [
二:redis
7 ^- i' B m t8 H5 Y
k8 g) W# w; @9 m2 y
2.1:maven中引入redis
6 L# R- y O+ m# T; f; X) R
; ^& D7 \9 N# N2 `1 G
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
% I, ~! f4 }+ y! Z
& f/ t \& g1 C6 R7 \
<dependency>
2 r- Z3 M* I: Q6 @. E1 X: H
<groupId>org.springframework.boot</groupId>
2 U7 P* e& ?1 E; @9 h. h" V
<artifactId>spring-boot-starter-data-redis</artifactId>
- s# Q* }4 f* u
<exclusions>
- R2 D( V5 y: Q* P4 n
<exclusion>
/ L$ S ^# |; N2 m3 n4 w$ p& C
<groupId>io.lettuce</groupId>
! O, C: Q6 z# c$ D' l
<artifactId>lettuce-core</artifactId>
; @& b& d1 N( l. c' a* G+ }& K# C! A
</exclusion>
% ?( ], r+ {5 n4 `0 r& Z* F5 i
</exclusions>
) d! e) C- D' E
</dependency>
' K2 f4 V _! b* M- l3 T T# h
<dependency>
0 m7 L" O8 g% n
<groupId>redis.clients</groupId>
/ Q: l W7 u) P
<artifactId>jedis</artifactId>
7 H. U, h! t- o* M# C8 [2 U
</dependency>
) H" l; t& S8 q# \- t% b3 q
2.2: 在springboot中配置redis
! U" {0 { K3 ?1 c: B
9 s. Z2 J. \: L* B' G7 ^4 T
import org.springframework.beans.factory.annotation.Autowired;
$ r& U( g h! H* d3 t. `
import org.springframework.context.annotation.Bean;
+ y) B+ y1 X/ S$ p
import org.springframework.context.annotation.Configuration;
- m Q* Q) ? z& M
import org.springframework.data.redis.core.RedisTemplate;
* `+ l+ A5 W+ e( O- j. a7 ?
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
5 s, i$ \5 v1 h; p
import org.springframework.data.redis.serializer.StringRedisSerializer;
( n3 c+ D6 B, V. j; g+ M
: c1 m* F; ~6 a; c& f; g
@Configuration
/ R6 x8 |& i% y& p
public class RedisConfig {
9 ^% `3 {' Z1 k _# R
8 w3 Z( y: l# A: v
@Autowired
3 }7 z4 `6 V1 M9 A" E. U( d
private RedisTemplate redisTemplate;
- X" G/ T% a6 C
: j4 a5 o. V2 ]8 o3 s3 w7 K
/**
8 y+ z3 \1 l6 k$ r# |; ?4 a* d
* redisTemplate实例化
1 |8 I: `6 M( D' J
*
: q7 F) l) h/ E( e; T
* @return
! A. Q1 X" ~6 q' H) \8 n4 s
*/
K0 w( F3 u! u. U8 W( i
@Bean
/ y) i: ^7 J& D1 r& M" ?1 m+ W
public RedisTemplate redisTemplateInit() {
* T- U# |/ a( D4 d6 n) Q
//设置序列化Key的实例化对象
+ o8 u1 w) F# S8 ]
redisTemplate.setKeySerializer(new StringRedisSerializer());
2 s# h6 _0 `% h7 G
//设置序列化Value的实例化对象
0 Y, g' l8 G2 u' \% G
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
: E" R/ l" p# r: j
return redisTemplate;
4 f) c/ W8 g8 c4 ]7 Z9 R
}
. o% E, l! j4 s0 `0 ?. z) @
: e. r. V0 x+ A, t
}
4 ?2 L7 E& N+ W
2.2:redisTemplate模板工具类
+ [2 [: ~! F7 B% `/ A
! `- n/ q1 P# Z2 @7 C: S4 Z; h
@Component
1 _0 A: c5 G7 h( Y G
public class RedisManager {
. G/ _( P# L/ \5 x* U
/ r/ z0 u9 ]8 b9 r
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
3 {. q/ [: t) C9 z
. Z( ~) q% W/ |; F. K# O# Y
@Autowired
. u( M6 Q3 d, C3 j* O
private RedisTemplate redisTemplate;
! K* X3 F* k3 C& k9 q: O' S, p
( |; t4 r6 D, O" R. [1 K' a' i
/**
) u# E, B, z$ X2 l) n. M
* 设置对象
3 o/ w% R% h( ]4 C" T; _
*
( C0 l5 y5 D+ V6 F
*
@param
key key
2 P( C0 b! K2 s. _6 D! a
* @param value value值
1 p9 o1 {9 J5 ~- p
* @param <T> 返回值泛型
( S, H/ \1 m+ x. U
* @return 正确的值:<T> 错误的值:null
) ~2 h3 _' z6 v5 w2 }
*/
8 z+ t2 G5 V8 X' X9 O, j
@SuppressWarnings("unchecked")
# n; P4 g* c1 }: W- z- M# e
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
' B2 e6 _+ |* `) g
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
! [0 T4 Z1 [& G: h3 l: a) t) T, K
operation.set(key, value);
5 `" |- T* y+ M. e7 q
return operation;
% j+ |8 g' |7 @5 C$ F- Q, w# L( Z
}
2 V4 o2 j+ y ?' y
0 E: p2 K% c+ V1 F/ Q+ a
/**
# y. o& Z. ]2 j4 v# I
* 设置对象及失效时间 (单位:秒)
4 I- E' l. A! d( b- R" p2 x
*
) J' L8 u$ Y' c: f& J; I
* @param key key
7 U! J3 k% B/ [8 h2 u+ m+ P9 G
* @param value value值
E* E# n1 h. E3 L
* @param <T> 返回值泛型
& }: M `" b; [ m
* @param time 秒值
- p3 z! g6 O: {" ^
* @return 正确的值:<T> 错误的值:null
( `. a) R$ N7 j6 W# I
*/
1 B$ M% b& U* f, o% r
@SuppressWarnings("unchecked")
$ C# r0 f# ^/ U1 b5 ^
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
/ K. C3 P$ q7 F
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
5 P+ e% R; X) F/ p7 }9 p
operation.set(key, value, time, TimeUnit.SECONDS);
5 V0 U: f3 u% c B! W9 l( ?
return operation;
* ~: w" V f2 V# ?
}
+ K) ~( E8 X, ^6 K4 t
9 A8 b8 ?! ]: }2 p+ c
/ y0 ]7 _3 d% v+ r6 }0 v. M, q7 H7 F% Y
/**
) w8 n% ]/ O4 P3 e7 R5 U
* 设置对象及失效时间(单位:毫秒)
" W2 y# f/ X% L
*
2 X- `" g1 R, D
* @param key key
. S# g# r) Q7 r3 v8 o+ Q' q
* @param value value值
2 p7 V# p C, D) [7 `; V
* @param <T> 返回值泛型
) u% g! O$ Z9 j
* @param time 秒值
" o5 R2 ^4 x7 W U
* @return 正确的值:<T> 错误的值:null
0 g6 V" m9 `. J+ c( ~" t
*/
: Z8 h/ ]% }( h- z; [# _
@SuppressWarnings("unchecked")
}( l# x* ~3 _6 S+ S: R
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
6 A7 S9 q1 Y' A8 k e; S
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
/ L3 m$ z4 h+ h# G7 r' f+ @. i
operation.set(key, value, time, TimeUnit.MILLISECONDS);
5 v7 O8 C, O7 n5 ?
return operation;
# I' n9 z( [: I% O
}
' ] `1 ]' {& G+ x2 F. [3 ?5 D
/ r" ?8 K% P, h( o) ]. w: l# e
/**
: B4 V' m5 c o
* 获取对象
3 s. x) w) V5 K9 {; r) c
*
" X1 ^0 A4 T" [ `) v- a0 k g
* @param key 键
0 x; a) |- I' ?, W' _
* @return 正确的值:Object值对象<br>
+ L* P1 [* P7 p( c' C( u" [
* 错误的值:null
6 S9 _3 |, k3 Y: E. n( k
*/
5 o, h+ c f, k. N' k
@SuppressWarnings("unchecked")
' x5 @% C! o' u
public Object getObject(final String key) {
' d6 L2 O0 E" s; |: l- h
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
* P0 q) `- ]/ y; D
if (valueOperations == null || !redisTemplate.hasKey(key)) {
; J+ [& v4 g) o1 A# a, S
return null;
' i0 j4 [" ?% p' x
}
, r$ h2 d% U( d, m% g) R; U
final Object object = valueOperations.get(key);
; w8 p4 j3 w' y2 i" j
return object;
& n0 _+ W y/ H# T% ] G
}
- D6 c# d1 C7 f( U" E+ A( g
# a0 S7 p& A1 e% h
/**
% _' s" N" x/ u
* 从缓存中获取string值
B. z( q3 k" a& V/ }, A* `
*
8 b# v& a9 u5 D" t4 F
* @param key
9 V$ d. a5 v @
* @return*/
3 F5 v2 v( s. v' a* }* i) s
@SuppressWarnings("unchecked")
& d' q8 z) \) [" C# [6 ^( E
public String getString(final String key) {
# x- Y- N' r. U* r6 b+ h, F
String value = "";
6 C u4 C4 V4 m# U
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
3 g# o( V% M) I& z
if (valueOperations != null && redisTemplate.hasKey(key)) {
$ P! U- n; b# g( ~; S
final Object object = valueOperations.get(key);
- l+ v0 }7 P$ j y
if (null != object) {
( S, n" e7 }5 Q& D: @
LOGGER.info("--getString--object not empty");
/ I/ e- D5 @1 R3 ]8 Z: {
value = object.toString();
# Q" x- G) V' C( Q1 S
} else {
5 T' ~0 N, D9 ?6 a" p$ w4 R
LOGGER.info("--getString--object empty");
+ F" y: v# v1 b1 h, R
}
8 B, h7 D( I( ^- ~
}
8 Q4 z; I) {% q+ T
return value;
( D0 `% {# Y8 S2 z, ^
}
" ~$ Z v5 l3 ?3 C
2.2:在redis中实现时间控制
7 A j; i& b: b: ~+ {: R
; C! q5 l0 w3 K) D+ p
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
) `/ J0 B; n4 K
; L8 \& S; T! ^* N
import com.youjia.orders.redis.RedisManager;
4 \) w- ]1 d+ J! O5 t3 G4 ~5 V- h
import org.junit.Test;
1 i' p& P5 N& E" ~
import org.springframework.beans.factory.annotation.Autowired;
% ?. ~1 Y9 ?* M3 _# X! d; g+ b
/ G0 E8 J/ ]7 m, Y/ {+ x8 b' g% x% _
import java.util.Objects;
2 o0 |) K- k; K6 C( i# T
4 z! M8 N0 H! ]4 ]4 Z
/**
) N2 K9 M4 O; `4 @
* @Auther: Yrion
& n7 v1 C4 t& w3 q" C# ]/ `
* @Date: 2019-01-11 23:36
" c p8 A4 e, [& O
*/
r) D9 m; I+ T$ E
* R) {9 p! Q( c7 c4 f
public class RedisTest extends OrderProviderApplicationTests {
6 T& \8 @4 G8 E7 ^% k6 V. ]
5 {9 t. M( b, o: J, Z9 c" d
@Autowired
6 z, V/ g6 p) U; Z: y
private RedisManager redisManager;
0 d: [# r6 e) C, u
! ]6 _2 A4 g8 Y- P& E5 G& y
@Test
$ s; I. d4 j3 n, x
public void test() {
@6 |+ S @0 u2 K
controlTime("10000001", 10L);
8 Y* y, k$ S4 i+ s" I$ Y% K# T
}
5 E7 t% o N, g$ Y
0 X0 z4 c/ s; r4 ]- Q4 |
public void controlTime(String requestId, Long timeOut) {
$ H$ u- G6 ]4 K& P
' b4 w9 u7 S5 Y! K7 [ S5 P
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
/ y; z7 w( t, H
return;
0 `) n) w" `, T2 w: [$ x) Y7 O) h0 P
}
0 @( F8 G* B# ^1 M
//something code
# q& n2 x) r( s9 S4 j0 k
final String value = "value";
9 y4 p L$ }: a+ H7 ?: h
redisManager.setObject(requestId, value, timeOut);
( }8 o6 M0 E. j
final long startTime = System.currentTimeMillis();
) a2 D8 a: g1 m: c" u f5 G: @* g
System.out.println("开始控制时间");
) N! X# E. y9 Y3 p
//start
6 W& F& \8 f- u$ x6 j
for (; ; ) {
) o1 i3 o3 b. n9 E2 c2 U; M. b
if (Objects.isNull(redisManager.getObject(requestId))) {
" b1 ^( N( b; a' j7 R; K; P
break;
$ ?7 t5 g4 q! p$ I
}
$ t/ w! m# b u& |: E1 f
}
- ~4 C! B& d" y0 v
final long endTime = System.currentTimeMillis();
8 X9 U" H, D( z# E# I
. g6 c1 x' ^# d0 f9 U; U6 e
final long useTime = endTime - startTime;
. n; k9 p, U/ g h
) `6 y/ {/ {) ]
System.out.println("一共耗费时间:" + useTime);
& @! E& q {7 n: B8 D
}
2 Z4 R# i# R- {; Z m! B7 `6 D; A0 \
}
( v/ W2 z! T- Z
outPut:
2 \% j4 _6 U. K% r: F- [
$ a# K! n4 p1 r3 T3 g
开始控制时间
6 k" ~! g3 K0 i$ c# @
一共耗费时间:10042
2 E# {% Q! W2 Q l% o0 m$ h
三:总结
1 K! [ l7 m0 ]
! ]! F7 r" P/ B% l( D9 K: q
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
* h4 X$ p9 N+ K" r
————————————————
" {- S! j6 `; Q. l9 x/ B
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
% W2 `7 j" j6 B* N; k! k' y
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
8 z3 S) s/ N; z! A0 R: z1 t
/ ? z2 M8 k5 i) D4 ?7 f
/ E7 N* b4 R, Y, g- I8 O
作者:
1661888967
时间:
2020-5-6 09:57
学习一下,感谢分享
. v( g+ D% a& p8 M
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5