数学建模社区-数学中国
标题:
Java如何优雅的实现时间控制
[打印本页]
作者:
杨利霞
时间:
2020-5-3 16:03
标题:
Java如何优雅的实现时间控制
8 J! Y, v; D, _. a0 h
Java如何优雅的实现时间控制
前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
4 f4 f! i2 Y7 J
- R. X% R6 x. G# k+ M, H) ^
一:时间控制的几种方案
# S( A3 I! C5 V' k
/ N7 ^( P0 D. Q; H
1.1: 从线程方面解决
" i X; `# p, t2 o0 g3 C2 Z
6 ~, w( i { H3 ?$ u% L
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
5 b3 [+ r; P) M; i2 I% e% X
( U t- k: M& x; y
1.2:使用Timer
x# j$ y: r5 l1 w3 e% u
' {9 p) \! F" m' D9 B
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
+ a0 H1 Q& Y: w" H5 F" _; U1 @
+ {4 Q4 [9 G$ |
public class TimmerTest {
" G; a0 R8 ~( D" W
/**
3 F. Z) d3 b7 Z) p7 d# i+ D3 S
* 测试方法
& P% [& @! f) H; D" P
*/
; p3 x0 ^9 ~" C
public void test() {
/ z# {# v$ ^# E
Timer timer = new Timer();
6 e0 Y9 x4 z8 w9 v4 d/ h4 P' P
timer.schedule(new MyTask(), 800);
% D3 d* N. j' e! z* s- s& G
}
9 b; h2 r2 \- n. u+ j$ v; k
8 g! E C, J8 l& ]( b( j- _
public class MyTask extends TimerTask {
9 q/ t D. y% d& R% s
) M# |; k$ H1 D- `
/**
% D" ]# d9 y: I
* 运行方法
# L, Y7 i& @- h g
*/
6 N" v2 \" f8 j
@Override
1 y# a, B2 [% A% p" j+ i
public void run() {
: w. Z' \5 l* O& l# [9 M
System.out.println("输出");
7 j; T( U" ^% [1 {4 t7 Q% L& z: q
}
9 r- x* N% `, F2 v
}
- m# w( w9 g9 c! a, ?
}
( v6 c; k0 t6 ^0 \
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
$ w9 O5 p. Z1 |$ P* }8 I
/ q& b) v5 h7 M- I
1.3:redis延时
* ~! T1 X; w0 @3 i' N0 P8 N
! X. M6 k% \- s: \5 }( ~; p }1 T0 f6 f& r& F
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
8 {0 X t0 a4 l8 [. w. Z) A0 ~
" e9 B& j7 P6 H3 e! M
% ]! D# Y" ^& w9 h; Z, k8 g% E
7 \3 t9 R( o' V/ D+ ~# z& V
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
; U( e2 ]% A; I; \4 q! I* O |3 \
; e7 ~" L$ o, K5 C8 p9 Y
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
# j! c: q0 }/ D. S- v
( P' H3 T. w0 o/ m
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
1 ~% H1 u/ S2 d6 @& w
& j; s- H9 ^; G) w1 L) ^. x/ N
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
6 p, E1 o. M& Y K! Q) U4 n
/ e. Y7 Q+ H9 J' s+ M6 P/ D
二:redis
4 v: ]; q: Q1 K. U; k$ @# _
7 \+ _# [) r. b/ D) d
2.1:maven中引入redis
, k$ U8 w) v* X2 W! b3 ^
5 _- a/ e' S5 }' Q( p5 R1 }
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
; W% i, A& J% }; b. i( [& v
5 W: r7 ^8 J% ?8 K
<dependency>
6 z F& p: v( b4 [; W3 t8 P
<groupId>org.springframework.boot</groupId>
" ?. t. t2 `4 \
<artifactId>spring-boot-starter-data-redis</artifactId>
w6 ]. D2 X: A
<exclusions>
( ^; \# r% j4 x7 O, L" [: Q( ?
<exclusion>
- N4 t6 s% ^" J3 u; O/ m
<groupId>io.lettuce</groupId>
$ v5 U9 C* C8 u3 x/ i% i9 [8 e8 m
<artifactId>lettuce-core</artifactId>
. i! m& o3 y, f* \( W: O3 I
</exclusion>
1 a8 S( w# U& R9 J1 \
</exclusions>
O9 [. E& b$ j5 _# W/ D; b$ L
</dependency>
( C7 I/ F! O. {/ b7 w* }
<dependency>
7 V1 }; W3 R; K# \$ ?9 L; y- V' _
<groupId>redis.clients</groupId>
K8 R6 E* |" l
<artifactId>jedis</artifactId>
, t; Q/ t2 }- S }! `9 x/ p
</dependency>
1 } T* n, s, e# c' M
2.2: 在springboot中配置redis
0 S8 e- h9 _* W% y* V/ |
% ^% c0 o, t' g7 m1 g+ w% S; D
import org.springframework.beans.factory.annotation.Autowired;
5 p- @& `0 A8 G
import org.springframework.context.annotation.Bean;
5 ^$ s5 g4 S' S+ B+ s- z6 r- D
import org.springframework.context.annotation.Configuration;
! e6 t+ W1 q7 e; p9 V$ k7 x1 I, d* k
import org.springframework.data.redis.core.RedisTemplate;
$ z( S0 m" U# g& u8 @" |, u
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
) u6 M& i9 z3 ^; K
import org.springframework.data.redis.serializer.StringRedisSerializer;
9 s( K# q' H: r2 a9 [7 k
2 S; j( _% l$ g I7 d% }( k
@Configuration
1 N" n. i. q0 N. D
public class RedisConfig {
. ]- `/ ^, A5 `3 Y( G8 T. q
' X2 t! m1 q# _: h# Z
@Autowired
( f% O9 v; l( a, G! d) L
private RedisTemplate redisTemplate;
8 R8 X# `5 j$ S! z ?( J% \3 G9 a. {
' y# s. o- R# `8 |3 W
/**
, m& P A6 v6 j6 k6 ^/ F5 Q
* redisTemplate实例化
/ T# T2 B6 m1 q3 {
*
: Z- Z/ t) l- U$ T7 d: n
* @return
* e2 x$ Y ], [* n* d% N8 W
*/
* X% L' }9 R+ I
@Bean
) z2 u4 d! ?* {8 ^/ ?$ H
public RedisTemplate redisTemplateInit() {
0 G5 u2 @$ u( R2 s6 J2 t3 q0 c
//设置序列化Key的实例化对象
* @' g3 u* n8 C8 j8 F; P: w
redisTemplate.setKeySerializer(new StringRedisSerializer());
! ~% |9 r" }9 d2 E4 K0 [
//设置序列化Value的实例化对象
7 @/ S; C& `9 v5 R9 X. t
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
1 Y: H' J' e$ c) r! p
return redisTemplate;
1 |* g2 B+ \" a
}
. q1 W! h0 m L, `
7 E6 X( p( g! N4 t) v* i
}
5 _9 N n0 j9 M4 K) x( ]4 y4 w, _
2.2:redisTemplate模板工具类
5 F o8 o8 i- ^' ^
3 o) j( n t* c5 ?- s
@Component
: [% d+ b% _$ Y4 E8 z
public class RedisManager {
% Y$ v3 d# B5 d
+ q1 \+ i1 ]6 s) q- _) |
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
- O4 v& v2 B' U
$ U( }. t) v: y
@Autowired
& W4 I$ a- z) G4 u" r2 `6 f/ j
private RedisTemplate redisTemplate;
- r# O6 ^# v: F2 p, W, A; a
" S# d6 b& p' |
/**
7 Q7 c/ P) n0 B) ?$ R
* 设置对象
% Z5 G& P, q' l7 W) b, s' k
*
4 L1 ^" A9 _! Z/ c! g, o. T! X8 C7 E; w
*
@param
key key
+ `! a' @4 g+ ~6 ?- J
* @param value value值
. E) P h, `) G3 J) m' ~: z, k6 v
* @param <T> 返回值泛型
. B0 J1 Y& {1 b' i
* @return 正确的值:<T> 错误的值:null
) M+ B- I+ Z5 P3 L/ G( H0 d
*/
, X: [8 ^4 Z) {3 R( {1 @+ P9 L! p
@SuppressWarnings("unchecked")
8 [, N3 p+ b% D
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
8 @. W% ^& j$ C: k) C/ p
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
4 ~1 [) v/ u% ] d. J* j
operation.set(key, value);
( e8 J, @$ z0 k2 q7 @$ ~ p
return operation;
" |* e6 S t% C1 ^
}
9 p# f% _* [; i1 f Q* A
, u! @) W7 Z% q2 S! v" V7 s; b
/**
) Z$ y6 ^2 K( w" u
* 设置对象及失效时间 (单位:秒)
9 Z. S+ k" H- h1 \7 g1 ?+ |; h
*
7 k$ E* W* m5 L2 d
* @param key key
3 z( |' ]5 B7 T. N$ t' }
* @param value value值
* `5 ]7 `! Z7 g
* @param <T> 返回值泛型
) s) H0 O8 h, S5 d8 V# C `& N
* @param time 秒值
- {2 x- K/ A' \
* @return 正确的值:<T> 错误的值:null
1 I, d, u6 r ]& d
*/
. [5 L, Y9 x H9 a# j- e8 Q/ N. H$ p
@SuppressWarnings("unchecked")
$ N) d" L4 j/ q' b4 n1 O2 t) Y
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
4 T) a* V, g+ F0 O' R5 S
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
2 ?8 @3 I4 N8 Y9 H O
operation.set(key, value, time, TimeUnit.SECONDS);
) P" N' b& k) `
return operation;
2 @ C% A' u' z
}
* L. |0 S& R: v+ Z
. ~3 \9 j& u" B# p/ T2 j l
& K4 c* I3 x) d* f
/**
0 d( `& j# s6 l) x* l- [+ G$ c
* 设置对象及失效时间(单位:毫秒)
( V& J2 h, \. \' q- W
*
) j2 ^) E0 J" m4 X
* @param key key
2 H' U) ]$ T; k* Z1 z/ D3 Y
* @param value value值
, V! j" H. x/ B0 ? m4 A$ w( z
* @param <T> 返回值泛型
4 T; ?3 L& } Z' x! R! Q7 a
* @param time 秒值
7 O" {2 A3 O. H' t! _6 e: L$ l
* @return 正确的值:<T> 错误的值:null
8 {- I e! L7 w, M! o8 ?1 r" [
*/
; o/ p( s' c/ N& b( v: r0 R
@SuppressWarnings("unchecked")
* k& A/ u1 c6 B; w
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
3 Q& u7 A/ t/ ~
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
$ ^' a, p/ U5 G) Y# L* \3 @* ]
operation.set(key, value, time, TimeUnit.MILLISECONDS);
' v7 c/ G3 \. E! @2 y9 j
return operation;
1 R/ j. _% K1 v# ?: k, s; h
}
) R1 z% y# @$ `+ a
% v4 v, Y% I' q; G _
/**
, Y8 V! Y4 r1 a8 A5 r5 D
* 获取对象
4 Q0 E3 |& _# z5 I$ i
*
, ?# g3 K7 n7 Y" [ J
* @param key 键
9 j8 S" P% L! B0 p& ^# s" c
* @return 正确的值:Object值对象<br>
& D+ h5 m! w; g0 c- @
* 错误的值:null
3 l0 G# b, |( w
*/
, T4 `8 Y/ W9 m( G4 E
@SuppressWarnings("unchecked")
3 F( N) F0 V0 I3 V' \
public Object getObject(final String key) {
: t- b, F, w- R8 W& Y6 `
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
" X+ \/ z1 f; n4 [: \8 p+ F5 l
if (valueOperations == null || !redisTemplate.hasKey(key)) {
+ u$ E E, A* ]( @4 K8 Z9 t
return null;
6 n. \/ Q# U2 z. i
}
- Z! K5 u* g# N, Q
final Object object = valueOperations.get(key);
8 M! c9 i$ k' H; f' {# H4 f( U
return object;
0 Y, p/ L0 g! }' j8 X. F/ D
}
- L& M7 W3 e I; z+ G
/ c5 z. ~0 k3 w+ @
/**
! f8 G- ~- Q6 A; U% V
* 从缓存中获取string值
" t7 t% }! j5 r9 e- C/ L
*
+ F" V- F, W1 h7 }0 S- v; a- Y5 u/ J, u
* @param key
- z" o6 n6 m4 I e! Q+ _$ b3 d
* @return*/
/ m/ c! M; q: _
@SuppressWarnings("unchecked")
4 a% P9 c# l, C
public String getString(final String key) {
; i" `4 [; P* X0 T+ @1 \
String value = "";
& d/ E. C- {. R7 q& E7 a5 ]
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
) ~" {' F+ F$ t! w8 A9 ]
if (valueOperations != null && redisTemplate.hasKey(key)) {
, _4 E+ t# H* S6 u/ g! K" R
final Object object = valueOperations.get(key);
% A, t! I4 W7 y: }
if (null != object) {
/ z) Q4 W4 ~4 [
LOGGER.info("--getString--object not empty");
( F8 n \8 ~4 I% m. t
value = object.toString();
# H4 R7 Z$ ] B
} else {
: l: L4 X' ~6 T! A6 P7 {9 y
LOGGER.info("--getString--object empty");
, f2 r9 f1 }7 V7 I+ u! K
}
) q0 `/ a6 Y* ~* j& M
}
0 W& ~( u! j/ \- Z) C8 `! l
return value;
6 x6 T" \+ Z$ d
}
" b' q% o9 h9 @1 w) K+ x8 p# G3 S
2.2:在redis中实现时间控制
# G5 e; F& ~6 w2 o( y1 i$ R
n- i, O' M( z( A
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
8 U! O8 |$ U! H% T2 @/ H) p
, P1 L8 r4 \+ Q: ~; N$ e* K0 c
import com.youjia.orders.redis.RedisManager;
# {2 G; ^/ Z/ X! |
import org.junit.Test;
' {( [5 C% W* ^! U7 o+ m9 d
import org.springframework.beans.factory.annotation.Autowired;
" q; o% F6 L+ R8 S+ t Z
3 k Z, a( n T! S# w4 r
import java.util.Objects;
" v. e* |2 }1 \3 @4 C* Y/ j
4 h8 _( h# `' A p& c |
/**
0 m( }6 O8 L5 U ~3 @8 e. V2 | N
* @Auther: Yrion
0 t( o! i" ~0 R
* @Date: 2019-01-11 23:36
3 [. M0 |( ~) E! H" R& c% I5 [
*/
) p+ T- P3 ^5 W
+ I4 t3 N! w, S" U$ j9 w
public class RedisTest extends OrderProviderApplicationTests {
4 ]( a- R# d' X" v5 f" ^
8 Q- X, q1 `5 a/ l R
@Autowired
1 g/ k9 ~, y8 u8 x7 k
private RedisManager redisManager;
; g! n4 r7 X0 \9 q* t
3 ?) b- B, X* g1 T4 j- h
@Test
! m) X9 i3 o0 s$ l- {; ?& Q; H$ Z% H9 T5 _
public void test() {
3 Z: }) M9 d- I$ G' V" t
controlTime("10000001", 10L);
. ]( G) _+ \/ z/ z! i
}
0 u ^/ m5 `7 A. H" @
7 B6 N+ E2 r3 a3 T |" m* r4 _
public void controlTime(String requestId, Long timeOut) {
6 a- x/ L# {3 c2 c6 q4 X
5 D- s2 J9 ~9 N5 l$ s! m0 n* H
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
6 }# g5 ?2 r9 o6 F# J& q
return;
o5 |5 ], Z+ B: h; l; [# V( H/ t
}
- f1 E# t% c% c& w
//something code
( c( a1 t$ Q3 A+ f% a
final String value = "value";
; A7 u8 o) u4 g- X% Q0 H: Y
redisManager.setObject(requestId, value, timeOut);
W6 ?& F" W! y9 E- j2 v9 k Z, ^* l
final long startTime = System.currentTimeMillis();
3 t" G5 C' \* w" u) z
System.out.println("开始控制时间");
: I7 m0 x0 b" ~; Y% P% ]0 E6 ]0 \
//start
5 W9 n# p3 C! U/ ~# K+ ` V
for (; ; ) {
; E8 S; I5 a/ Y% m7 o3 n! d& @
if (Objects.isNull(redisManager.getObject(requestId))) {
: D& B8 D0 i7 j- L L) m
break;
5 I( L& p5 b( M
}
9 x6 p+ J( s$ t( F# C6 o
}
: C4 b: ^8 U$ N" ?7 X
final long endTime = System.currentTimeMillis();
! z! N" m- d/ X- L+ z
$ M6 w2 Z- y; b+ w" i1 r
final long useTime = endTime - startTime;
- ~: J0 b; R1 b% b
' I" W7 b) f p2 ^3 n9 g- \: W, M
System.out.println("一共耗费时间:" + useTime);
& e% H8 t: L. \; C
}
1 b6 r) H7 I$ K. l {
}
& v9 J3 N" |. Z3 P: S; j9 P1 v& r
outPut:
- }& h* S) h! ^4 X
/ K/ ^4 M/ L" T4 D9 s K& e: H
开始控制时间
# P9 B+ j. `8 |1 t0 N
一共耗费时间:10042
5 o3 J' U3 y, ]" e
三:总结
% r; o! h. q$ c8 w! z
2 _* U0 c1 J6 G5 j M; a% s2 T
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
2 d, D; ^2 Y: ^4 J( |
————————————————
3 |0 Y7 [ ?# K/ ]/ U' q
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 T" _0 Y9 u0 s- |) H% a
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
4 Y- U$ |! h( P6 B$ F
X( ~0 z% W% j: h5 O0 I5 ^
/ h5 a5 u% G2 s k0 k+ s( Q
作者:
1661888967
时间:
2020-5-6 09:57
学习一下,感谢分享
5 ~4 t; @8 X, _$ H9 H' k6 D; F- D
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5