数学建模社区-数学中国
标题:
Java如何优雅的实现时间控制
[打印本页]
作者:
杨利霞
时间:
2020-5-3 16:03
标题:
Java如何优雅的实现时间控制
~! l5 {% b- f+ `& W4 f
Java如何优雅的实现时间控制
前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
7 `" ]# i8 L- \ { ]; }
, Z' N2 A2 J, D2 K) ~
一:时间控制的几种方案
% I7 K$ [6 R) ^7 L" _2 p4 Q n" o9 i9 N
7 v: ~5 i+ y# }* h: I$ `9 n
1.1: 从线程方面解决
, ^, W# {/ b& [$ i
" B3 ^5 y( y6 [
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
/ n4 ?: G* X- s# }# K
* S8 g+ h4 u. g( I5 w" o( ]' G
1.2:使用Timer
2 U& {3 C2 B, t) z4 j \: A' J0 K
) [" l* j+ M+ k3 f U2 w
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
- t0 w/ ~4 J1 g& A2 W$ e
1 s8 L7 C; s& Y: Z Q
public class TimmerTest {
1 R6 q/ [2 s2 M P6 I
/**
! q9 g) x7 R6 l- {2 `
* 测试方法
+ h3 |/ V' O, R3 D
*/
+ r/ h" ^. l0 [/ t$ _4 v/ \
public void test() {
0 ^, D$ @+ `* h4 f( p
Timer timer = new Timer();
2 s7 d0 @& F9 R; F0 L1 j
timer.schedule(new MyTask(), 800);
8 t7 W2 i3 P" L- x! e7 l
}
7 l2 h: V+ B8 e9 S; D7 G7 K
: c% K# U" b; e+ R# q% k: ]
public class MyTask extends TimerTask {
! n* A1 Y; H: D" C( ~
, I9 p `! p! O' Y" y+ n
/**
. C. d' _. j% Y; K( T
* 运行方法
7 D- g) P: m; L7 @! e6 j+ R
*/
9 G: ~: P+ V) R! V' ]3 E
@Override
1 p% b3 h$ N5 `$ f6 t2 v7 D7 `8 K
public void run() {
; I" n+ P6 ]/ ]& V
System.out.println("输出");
5 i' j; K, h0 ` j G
}
5 |3 {$ F* l5 a( @- X
}
5 v+ A; |6 D: |) m
}
: ?; d/ x; |1 a ?# O+ A
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
1 n& l' d; O. K* B: R) [
" d k; {" M1 _* s W# \
1.3:redis延时
' M3 G2 B5 h* g$ ?' n5 g4 y5 _
! S8 w2 L4 K* B* G. L9 n( ^
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
) P+ R6 B2 \7 z
8 U& s r6 ?6 x/ \5 ~' t, Z
3 \+ E. g1 X9 Q- y/ q* r0 i
; J' d) J' M" h% {" P
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
: q3 `4 H9 q4 K
0 Z% R! u. E0 {+ U' ]' ~
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
) p# [( i! Q/ w* y- p8 \0 y/ Z
) U6 S8 C$ M( Z. _
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
8 J1 P' u# i) K! ?1 L* c
9 B8 I+ I: m+ I: `7 S
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
7 {$ b/ N9 e3 q; f5 d/ J
& x7 \% o4 G3 j+ X$ R
二:redis
& ?- q6 v( d- H- u3 G- g6 J) S
5 f$ o3 D% P/ N) L' {
2.1:maven中引入redis
" V% `; o9 v( `' M9 b
4 ^# X. G+ |, ~6 ]
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
! D4 O& h" W# U! M
2 T4 d9 |" F9 c) ]' p) |
<dependency>
7 D7 ?9 {8 P8 C7 o6 }
<groupId>org.springframework.boot</groupId>
' ]1 e7 r1 q! L( j* ]
<artifactId>spring-boot-starter-data-redis</artifactId>
- {/ L0 e" M+ r8 o. h' H( C
<exclusions>
+ \/ J' r) N& K7 ?5 _7 H) U
<exclusion>
' c7 N& }! k/ k7 t; V) b0 a8 i
<groupId>io.lettuce</groupId>
! ]2 C+ F5 d; d. i4 {+ |4 |
<artifactId>lettuce-core</artifactId>
+ m, T6 n3 A V) T0 M/ l1 X
</exclusion>
# v. a5 K/ z& n* m+ k( _2 f
</exclusions>
3 O7 x3 ]8 P3 q/ J
</dependency>
4 R M7 N3 K8 P) }# \
<dependency>
% k6 p* [3 I2 M! h* C" L6 B
<groupId>redis.clients</groupId>
+ g! \5 C- D L/ x9 O) W# X
<artifactId>jedis</artifactId>
' C, V* P6 b+ T+ `
</dependency>
3 m0 d, V1 y6 ` z3 e' d* s
2.2: 在springboot中配置redis
' L- u4 ^" J# n, O3 P7 ^* B4 S2 T
: z4 z/ c7 I5 e: `8 \( E0 l; K
import org.springframework.beans.factory.annotation.Autowired;
# F6 j4 J/ b* Y
import org.springframework.context.annotation.Bean;
& |8 s; \4 W" |9 Y* u1 J
import org.springframework.context.annotation.Configuration;
`6 u6 T8 l5 X% P1 t" K7 _( f1 ]
import org.springframework.data.redis.core.RedisTemplate;
! o ^- \/ T' }3 {* r
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
5 n3 V- ~: {, S9 T5 U+ E
import org.springframework.data.redis.serializer.StringRedisSerializer;
. ~" t. q E* N: v+ Z
; Z1 F% c5 Q& p
@Configuration
5 [$ u3 H7 I' [; i' i2 X
public class RedisConfig {
3 C! x/ @4 s1 X% ]" r$ y5 c& G' u
0 F8 r; \9 \% h& e4 u" r
@Autowired
7 }& I; X& i3 M3 w/ `
private RedisTemplate redisTemplate;
" k2 W+ F& b( l4 U4 s
% `6 {$ V1 Q' u# ^% [, L
/**
o; T8 i* N: L6 Y& r
* redisTemplate实例化
8 [9 D$ o/ G) {, s
*
6 m" l L6 ~. u! b$ Q
* @return
6 d: ^2 M" _0 z( @# M: {
*/
2 r3 Y4 r) P' h% L3 z4 i3 g. `( G
@Bean
6 Q# D$ S5 E" E: H; L; D
public RedisTemplate redisTemplateInit() {
^2 e6 Y% W) ?# L3 c
//设置序列化Key的实例化对象
, @$ x8 X2 E- r- Y- {; F
redisTemplate.setKeySerializer(new StringRedisSerializer());
; l3 n% d- s0 I- J2 G) z
//设置序列化Value的实例化对象
) k" g3 W1 c6 Q# J5 H5 H+ J) l) i) Q
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
) H) ]$ W4 T# B8 z& S
return redisTemplate;
1 b* X1 K9 D% x1 }4 M
}
5 V6 ]2 p9 g9 ~. z
# b8 D0 e O/ y$ s& ?, @, v
}
9 J/ y0 R' O% Q
2.2:redisTemplate模板工具类
4 c: ]. l& D6 z# h. i5 L! g
: X# l/ S# d Z% h1 n
@Component
1 M" w* Y! E& B9 a/ p( Q6 \
public class RedisManager {
+ z0 r7 F4 {% A V* g! m& ?7 K
& }- p9 ]! P9 Q+ s
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
* r& n; V) @, E* e+ g$ n
+ |# l3 F. C$ S" J# ?) b b
@Autowired
1 z6 y" m9 L. R
private RedisTemplate redisTemplate;
5 b$ V5 J6 c& k" O) D
' n: f2 z* m- R
/**
0 N1 o h( D5 X2 V/ }
* 设置对象
) A+ g7 z; U1 Z5 @8 E% K% ^: P
*
% N* l4 P* S4 C& N& K* D* ^
*
@param
key key
+ L6 z4 B, U6 y9 m/ q2 Y
* @param value value值
; \% ~3 c _( r/ h0 S, x6 o
* @param <T> 返回值泛型
: b* J$ s9 B! F$ r0 d
* @return 正确的值:<T> 错误的值:null
# z. N. ^0 D; S$ O7 T' b
*/
: y T$ ?- b& _
@SuppressWarnings("unchecked")
1 S x. y- I* i. b9 _" Y M
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
( k& y/ }$ _: X9 B* m
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
- c, w: f7 n: M+ t' j
operation.set(key, value);
) Z1 }5 R, h* J5 v* {8 V7 u$ g
return operation;
# g! f }! L) R: w3 A
}
) U, Q4 Y. F7 Z2 S& e0 ]. F0 R$ p
" n4 r9 M( ^! Y
/**
3 Y/ ]' m( @- b" v8 ~
* 设置对象及失效时间 (单位:秒)
7 f* G- ]: e, L( M7 p
*
; ~* d w1 x% K- |- e- f( A( p
* @param key key
7 _$ ]9 c& [7 E% a( c/ k9 e+ z
* @param value value值
5 L$ j% R0 h/ n! J% n" @
* @param <T> 返回值泛型
# `2 f9 X8 R' X( `, E3 C
* @param time 秒值
$ D8 H3 l" o5 e- G# F; |5 |# k
* @return 正确的值:<T> 错误的值:null
1 @' i, {0 E/ D& |
*/
' D7 P7 T0 R! V% \
@SuppressWarnings("unchecked")
; B% t# Z$ D( \0 l: W
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
* S2 p* b' p/ m/ h/ O
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
) ?+ c# V7 X9 \
operation.set(key, value, time, TimeUnit.SECONDS);
0 F6 b* y3 j' l- a+ A
return operation;
& I5 u6 R7 O( Q3 R8 r: F
}
) X! U% a8 U$ r$ C1 J0 T Y
- h6 |1 A, {+ M" O
' V% `& K9 W2 U# t
/**
* L- K5 _7 M# \0 L+ s# Q; O0 N
* 设置对象及失效时间(单位:毫秒)
0 j3 s5 n$ N m
*
2 G. y- Q7 U1 Y
* @param key key
0 q- `& w8 R2 Q
* @param value value值
- l' r6 g; }7 g
* @param <T> 返回值泛型
+ i' A$ S1 L# _
* @param time 秒值
0 ?+ i: h. l- ~. l# L6 A0 _
* @return 正确的值:<T> 错误的值:null
! w; _9 |6 E; O- [1 d
*/
6 r- |0 _% h1 h, U3 \4 ]
@SuppressWarnings("unchecked")
! E: F+ U7 F4 C& M0 r, N2 U
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
1 v/ W$ b+ i* [* f" r
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
- Z9 O" Z8 S; F! A+ D! h
operation.set(key, value, time, TimeUnit.MILLISECONDS);
( q) H4 g% T$ c) Z0 w
return operation;
9 c" w/ `, j5 i, k% Z1 c" O
}
9 j. l$ f1 H7 y' T
5 P2 h1 M9 d8 ?
/**
! K. [2 R$ X/ ^+ S' C
* 获取对象
# B c4 V0 b7 y' b
*
% ^ ^7 N, L7 w/ o, e4 F& }
* @param key 键
) K3 ^/ g# H+ w
* @return 正确的值:Object值对象<br>
4 ~8 h1 q+ Z+ L+ t
* 错误的值:null
" c3 i E% y4 Y0 Z8 H
*/
: P* F: r {, s7 v
@SuppressWarnings("unchecked")
9 v- k ^, c8 p; O+ O) ?
public Object getObject(final String key) {
+ \8 n0 f$ z! |* b. }+ N0 t
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
$ M4 R0 ^" y* F( B
if (valueOperations == null || !redisTemplate.hasKey(key)) {
" k# w* Y: F! Q1 U3 c
return null;
, y m" Q; a7 I7 s0 [8 b8 F
}
0 O, k( I1 _$ V+ O. l' N
final Object object = valueOperations.get(key);
O$ L+ V: G* P1 @: A6 l% y
return object;
2 \9 `; E9 O' @+ m N1 P$ ~
}
' H2 f+ |# G* c% d
. s* ]6 H" I: a. Q
/**
+ j& Q' Y4 I$ }
* 从缓存中获取string值
. k3 e! l$ e0 \
*
: N+ O/ k; J7 }4 W2 q* E% N2 h8 ]
* @param key
: a- R9 j* L! q9 E4 z3 z. A
* @return*/
$ h+ X5 W' ?$ i$ N* p; Q. r
@SuppressWarnings("unchecked")
2 q1 ^( _! e1 W0 _& w" m5 {
public String getString(final String key) {
" v- z* X2 i5 x8 c- m% {& @- }
String value = "";
" k$ x6 b, T9 X
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
+ o2 n2 b" D# Z
if (valueOperations != null && redisTemplate.hasKey(key)) {
3 C. J6 |4 }. l% C# m; ?
final Object object = valueOperations.get(key);
+ d" l2 W3 B% ?! [! C8 \/ b
if (null != object) {
1 Q9 h$ _9 _8 ?7 g. _
LOGGER.info("--getString--object not empty");
" ^2 H. S* Q& v% b
value = object.toString();
8 ?) h8 \" ] F- }8 V/ F9 z0 i q$ p# U
} else {
6 m6 d) h3 D+ h& ]0 ^
LOGGER.info("--getString--object empty");
p5 A9 r' ?) D# I M; D0 ?: \
}
1 ]' s4 Q/ y. g+ _+ a) ?- K
}
) K- T% J) H3 d& W2 e; ^
return value;
: w: L- i6 \6 l' F8 q
}
N9 q/ b3 [/ ?( X0 V: S8 ]' L2 H* o
2.2:在redis中实现时间控制
# ]* a* M. @6 J
7 N3 G: ]9 j( ~5 s, R1 a7 i
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
0 W* c# Y3 g/ t [$ s$ c
$ }. w! I; |8 t6 q
import com.youjia.orders.redis.RedisManager;
' b+ o% z+ U8 a; Y6 K1 L
import org.junit.Test;
" R4 W; C5 ]1 K9 @) O
import org.springframework.beans.factory.annotation.Autowired;
s! I0 t q! _' J7 w
6 i o8 _- P+ B+ U
import java.util.Objects;
$ M5 P8 a9 ?: O# X6 ~) w5 V
6 c: A$ z; c$ Q5 v6 ?! e
/**
8 A0 \4 D# }1 y" W) q& O
* @Auther: Yrion
8 Y( e- G2 A6 @& A" U
* @Date: 2019-01-11 23:36
8 F( }8 o$ }! r% N) h% ]
*/
- a" x, ]$ ?2 A. n
! h4 r) b/ A T ^
public class RedisTest extends OrderProviderApplicationTests {
& r: j. M( i+ Q' \# H
5 a8 T1 R5 s1 z ~8 V
@Autowired
1 v7 u) I4 a: O2 i. s K2 s' k
private RedisManager redisManager;
! f- u( ]6 A6 u$ L( z2 v& |; o
4 U+ c) J9 v( j: j2 F& e
@Test
( c' F$ ?! I! G1 G+ j
public void test() {
7 ?6 n) J6 Y: W8 b5 y
controlTime("10000001", 10L);
$ f& ]' ~3 t" d# X2 G; ]# @ B
}
8 k& Y, z! k3 O) |9 Y4 L) |3 G
8 B- |5 Z) W3 j9 `8 M
public void controlTime(String requestId, Long timeOut) {
: F: ^! T3 x. s M3 i' i3 N8 ^8 R
. ?7 o9 k' A V0 f7 O/ M4 _' L
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
+ P! }: r* }/ ^: s) m
return;
" |& u/ y _4 a
}
: b. C4 N j7 e
//something code
% }! ^ ?' s# h
final String value = "value";
. o: P+ d i M4 c' Q
redisManager.setObject(requestId, value, timeOut);
7 K2 T6 G1 k! N' C
final long startTime = System.currentTimeMillis();
, V% Z. z' u3 m; q- |
System.out.println("开始控制时间");
$ i. T( l" D) H8 l$ t
//start
/ `! G" g Q9 _5 j$ E5 h' O% J% i
for (; ; ) {
, c7 x, D0 D* q+ b5 s- H
if (Objects.isNull(redisManager.getObject(requestId))) {
$ y! S; i1 Z! F7 U8 r
break;
( K* Z0 U, X7 a$ I8 X% h
}
]) }+ J/ X( j$ J3 |
}
; d/ j1 `" b) S7 |, m. {" q
final long endTime = System.currentTimeMillis();
9 H2 U* M: e, i
5 h% J9 r& k1 _+ h6 @
final long useTime = endTime - startTime;
( ]5 O# Z b2 P& W5 v% h
) l) c" L1 C4 T, m# x- y
System.out.println("一共耗费时间:" + useTime);
: Q+ q, F9 y* B) q+ W' C* J% I& D
}
+ V7 d8 O0 {0 b
}
( ~$ P+ }0 g4 p# o( u( ]! L/ _- a
outPut:
7 D: t% H, N/ J- r9 v
+ i+ Q- e* X# v1 n8 f
开始控制时间
* V3 X% j1 ^) ~" j6 s+ j& j8 E
一共耗费时间:10042
: G* D) |7 Q; j6 {) h
三:总结
4 D1 U4 k: o8 O- b2 I% s3 c5 t
0 f8 x5 M5 E( |# \$ ?5 Q3 B9 q
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
P4 I R) x* N2 K# x
————————————————
% G* C6 R2 [8 g$ H4 Q
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
7 Y; [2 @. E& U" A* {8 @! O
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
1 E4 ?; Q0 A# u* k
$ u) K1 x0 F& o; i7 S. H I
$ c6 z& V% d' @6 {: x# [. H5 F4 n# T
作者:
1661888967
时间:
2020-5-6 09:57
学习一下,感谢分享
8 V* {9 L. e8 V1 H
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5