数学建模社区-数学中国
标题:
Java如何优雅的实现时间控制
[打印本页]
作者:
杨利霞
时间:
2020-5-3 16:03
标题:
Java如何优雅的实现时间控制
~2 y- ^7 j9 b0 C
Java如何优雅的实现时间控制
前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
, `5 n0 ^' r ?/ m+ i! B
" Q0 b; B2 Y+ o; e
一:时间控制的几种方案
- d2 O4 @( U; f/ D4 x
- I5 b3 }0 h" u: g3 a
1.1: 从线程方面解决
: R# ?8 g! n) e
5 `3 R) I" o: T3 V1 a; t
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
' l2 j1 ]( a( _& H
1 [# y* j6 Y2 U0 a
1.2:使用Timer
& I# M2 ]$ w5 O% N Q
8 U5 b5 r h9 Y+ _
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
3 j/ p4 F' `) n+ A* ~- h
/ D* A: d) {) Q7 ]9 O4 u2 O
public class TimmerTest {
5 @1 {( S6 q8 w
/**
- z0 \, u" j5 g
* 测试方法
' {2 E5 D0 Q$ O, b5 \; l
*/
# Q4 x% U- i. A4 a E6 _
public void test() {
9 o' M/ b" L5 A; I
Timer timer = new Timer();
: t& J0 T2 n8 _: U
timer.schedule(new MyTask(), 800);
# P% _" {! Z# `# ~7 G8 g
}
9 i$ [ k8 O- T0 _1 I) U4 z
+ S, ~7 W, \/ R" ^. S& ~
public class MyTask extends TimerTask {
* r5 r5 C. F: ?$ l5 {$ D4 A/ [
% w$ B% C( n% _3 w; r& {
/**
- k3 [& J9 j, T' ^5 Q' h0 y; f& i
* 运行方法
4 U4 P+ c1 F" v
*/
- \% S7 e- L3 z7 T$ C, n/ u
@Override
, a- R# U2 P: i' ]9 M7 ~7 D+ v
public void run() {
# z/ c) Y+ l/ A4 G8 h
System.out.println("输出");
% P8 T( [0 x* Z6 p' G) ]: B
}
+ j) y+ g, Q" `
}
! P/ G, w, ?, t; t/ L; ~8 V
}
+ U( x& y. p% P4 S) u# C
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
" a/ q% m4 y6 _- N5 C
& R) @2 L, g! @/ V; j K
1.3:redis延时
2 g8 ]2 p, Z. }9 G5 ]& ~! p/ l" k
) e) j# D( ^/ c- P2 X: o- [0 H
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
# H8 j$ c: {3 F2 G2 j* ?/ A
. u. Z: X- U0 z2 L6 z
2 @. b& G! j( e8 Z1 @
% B9 n; T6 w6 k& P% y
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
I) g# n* Q- v1 F6 |* `" q' m
; c4 o4 {8 T. m4 j9 e( Y" s8 Y
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
0 Q6 Q/ |( S4 }: V; w
4 X+ ]0 t" w. T. D% s$ ~
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
$ r9 v, _! B- g4 h
5 z) e$ e: C& I1 k2 h# p
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
5 M+ ?3 _' {/ m7 y3 f# ^- h1 K
2 H% u* O) t( q+ [3 A6 a
二:redis
" E# u. J! e8 t) |1 ~& h" |
! A8 |% V! H- G O" p
2.1:maven中引入redis
+ _3 d0 n2 {9 I9 |- K5 u
1 y# _- A, V0 e
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
2 d4 e6 d8 ?* s- ~8 b+ N
% J2 m4 b. J% l
<dependency>
/ D I1 A8 r$ t" v+ ^6 F5 J ]
<groupId>org.springframework.boot</groupId>
/ o) P! W( \ L' ?! I% \3 a
<artifactId>spring-boot-starter-data-redis</artifactId>
% @; d' x6 `) D) d4 s# k
<exclusions>
. G8 n. y1 M/ _
<exclusion>
/ N5 w9 {; R0 g1 R
<groupId>io.lettuce</groupId>
6 g4 L4 {6 @6 q) v& H0 Z2 Y7 m
<artifactId>lettuce-core</artifactId>
3 G' H& b6 x H& \
</exclusion>
2 ^$ B& f- ]# E) e c- u$ X- `% s) J9 p
</exclusions>
* \; g4 D$ |. N! T8 b4 A
</dependency>
8 b5 A) X9 _' W6 l! \" Z3 v. ]
<dependency>
# |0 K# |7 x! L C
<groupId>redis.clients</groupId>
3 m" |- I9 l+ |0 s$ F+ Z
<artifactId>jedis</artifactId>
7 K1 P! q# g+ X! e
</dependency>
% t. K) }9 S ~1 h* M; ~* G$ c& A3 G
2.2: 在springboot中配置redis
- J* A/ ?' ]! r# U( L
8 C( G% P; ~ F" V3 n
import org.springframework.beans.factory.annotation.Autowired;
6 z8 n" ^) G! h
import org.springframework.context.annotation.Bean;
+ U4 r. u5 W7 c7 _ M# y0 e
import org.springframework.context.annotation.Configuration;
; d& A) B- B. |# i- t. |
import org.springframework.data.redis.core.RedisTemplate;
& _% q) P* q4 Q+ y# _
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
3 G, a% t1 e- \8 d' U* |. l
import org.springframework.data.redis.serializer.StringRedisSerializer;
; ~5 f$ S- o; O
* q- r6 F# \; x9 t
@Configuration
5 G4 z/ F1 r$ u6 u- j; V
public class RedisConfig {
0 z3 T" ~" v- S8 b+ E5 S
7 l( f- O5 @: L4 V3 `
@Autowired
: I( j' y! Q4 |( w* c
private RedisTemplate redisTemplate;
/ j; q$ @2 \" m7 d, T6 r- O# H
( e! l$ r N% n! V
/**
# c5 s" V( D% Q l
* redisTemplate实例化
, s7 e# m. v* y. ?
*
7 Q7 u) s1 V2 `- u! W4 h9 ^
* @return
" R* {9 r1 { r" }7 X! n
*/
- M/ x) {# w" [& j8 u! W
@Bean
- `# f& ^" X+ D6 \& @) \% C
public RedisTemplate redisTemplateInit() {
' n8 y( B% n1 l% [* `; x" P
//设置序列化Key的实例化对象
2 l h* D" s0 S$ a5 s+ X
redisTemplate.setKeySerializer(new StringRedisSerializer());
: O6 c; D& D6 n j* |- p* C
//设置序列化Value的实例化对象
% r6 `( m3 _ w/ g6 y* F
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
3 q, P8 T9 _9 Y0 U& X
return redisTemplate;
|! i* O% F2 x0 d) U
}
8 J, P& W) `' U% H- X; {
* U9 b. z2 f6 X
}
4 S# E+ m1 ?' K
2.2:redisTemplate模板工具类
% o% ~3 S. S; ~2 [ d
0 p8 J$ m: v2 D0 t- D
@Component
) p J% u4 ~/ e6 Y/ B2 b
public class RedisManager {
# Y( S. k" b+ A3 p* j2 ? g2 X
/ U H6 @' S6 E6 V
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
. \5 e6 x9 _4 P8 B& F2 a- r& z/ Y
% q5 d4 [1 e1 ?$ y4 i Y. D
@Autowired
% q+ T0 k9 G4 @# M7 t* ]
private RedisTemplate redisTemplate;
7 k3 u0 Z/ C: u
; } Q. v7 \; N" y: F( a
/**
3 w5 o6 F. p* P, c y& Y; s
* 设置对象
4 A% d* h( u. Y2 t, s
*
4 H$ _1 H# r1 Y1 f1 |/ `
*
@param
key key
; Q0 j2 J. Y( D
* @param value value值
" b$ ~& j1 w6 I; w+ x. A4 d8 Y
* @param <T> 返回值泛型
8 j8 D. w" \1 R
* @return 正确的值:<T> 错误的值:null
) R n( V' m; }. C8 O+ O# n# _
*/
! \- ?0 `4 g) D+ b+ F
@SuppressWarnings("unchecked")
3 Z1 j+ F( G* Q# W! s" [9 b
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
& Q- _) w2 H) m. K
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
7 }( |# C/ o' S8 x5 x9 o. b, ?
operation.set(key, value);
; L% |2 x: _( T; i
return operation;
) E6 ? K% i0 R! c2 ~& \$ c
}
1 k# F6 J, o2 q5 v* O) i; i
* b% ]* g8 t8 w2 T5 [6 a
/**
" l6 Z' P, a8 z0 a. X
* 设置对象及失效时间 (单位:秒)
- i) A9 d: ?$ C/ [- S' o/ o2 {
*
0 I( ]0 z# o- e
* @param key key
% k; ?7 t! N% h
* @param value value值
! a/ E- g: y0 A) d( i
* @param <T> 返回值泛型
2 e6 E8 }6 h3 V( F) D* e/ d
* @param time 秒值
7 {; Y4 W) M; ^+ X0 Z
* @return 正确的值:<T> 错误的值:null
" @/ V# P. g/ x
*/
- R* ^" T! ?3 m* w' w! x7 a& s
@SuppressWarnings("unchecked")
, Q* |* j; |: V. F" j; [
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
) D! J( I7 K, d! H
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
' g. O+ R+ }# Y* ^& n- Y
operation.set(key, value, time, TimeUnit.SECONDS);
1 h- r1 c4 Y g4 [3 Z
return operation;
, v: H2 \5 d! q4 e& M. }
}
8 K' g) F# @6 o: m
, u3 }9 J/ r( N2 ~' {
$ B6 c6 {$ n, \$ F J
/**
/ W, D5 \; V# O1 C
* 设置对象及失效时间(单位:毫秒)
+ R; w l3 |3 z8 ~1 n1 V& |0 ]
*
# x0 }9 G& f( l" R3 \3 m
* @param key key
4 w- N3 X8 P$ x y
* @param value value值
4 R& Q9 ^. |1 n" U; d2 F
* @param <T> 返回值泛型
9 A. X/ J: V% g% x( g
* @param time 秒值
9 O& s( Z% _: K; r/ P }
* @return 正确的值:<T> 错误的值:null
% }- Q0 G# m. }
*/
2 v! [- Y! T! U* A" K& X5 I' Q
@SuppressWarnings("unchecked")
! z2 ^' I. x# q0 a' V$ X, z: O
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
- t1 Z1 b& B+ \
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
9 }( s- ^1 \( ^3 Y; w& f4 f* z; ^
operation.set(key, value, time, TimeUnit.MILLISECONDS);
2 S! C8 W) a4 U" [' y* y7 m
return operation;
- a6 a0 a& W1 W. |; V
}
3 s3 G9 ]9 D% |7 V" o. ~
$ t+ _( |9 P3 x- R: v0 C! \
/**
1 G& s: _7 \5 L4 |# b; Y) l
* 获取对象
, [4 ?- x6 n' e9 f6 j
*
; k0 b1 _; o+ k* S1 N ^' {
* @param key 键
% O. N: C: ^+ ?; L( `" g; v
* @return 正确的值:Object值对象<br>
/ ~, p! i/ f( M+ T* \' g) ~
* 错误的值:null
5 l; Q# b9 E& d! |
*/
# w2 v+ I& [, C8 A- z2 M
@SuppressWarnings("unchecked")
+ o6 J6 a, C- C/ |$ A
public Object getObject(final String key) {
+ }0 ?! R) I6 a& r( y& e, c
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
% s* E! w1 g' ]2 H: |9 [
if (valueOperations == null || !redisTemplate.hasKey(key)) {
- }9 b% E# _6 d4 A
return null;
m' K7 a; U; d. p- F% u, M& j* ?
}
! Z6 t" b7 h& B! f: a- }0 u
final Object object = valueOperations.get(key);
8 s+ b# ~4 j) M9 R
return object;
) ]0 p; L" \; l- I( b6 n' i
}
' Y9 b8 h3 K5 t& v* s8 ]3 E' k
5 |! b( }( z! T6 P& G7 `8 G
/**
3 z$ X u- c# h k1 A
* 从缓存中获取string值
1 E K4 U8 I1 y9 ~
*
$ U w5 G9 z* y y& N
* @param key
" D+ V9 @" E, ?" D+ a( ]3 L8 S( t1 L
* @return*/
4 J: }1 U. v: r- d- x+ ]
@SuppressWarnings("unchecked")
" M; k9 C5 A1 f
public String getString(final String key) {
1 D0 F1 d" }8 i- B( g: ^
String value = "";
! D- ~9 w: ?! g+ S. s, M
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
* @! [2 p* d- W- f4 D
if (valueOperations != null && redisTemplate.hasKey(key)) {
3 p# L/ ]+ E/ u2 _7 Z/ @: e. T
final Object object = valueOperations.get(key);
& P7 S, f5 }! h8 K9 A1 h( _( [
if (null != object) {
q/ ?& K; P! M
LOGGER.info("--getString--object not empty");
3 K' @+ f, Y i$ D) v5 U- [
value = object.toString();
# V: i, Z7 @8 w) Q
} else {
. ]1 I, e- V. D6 }8 \- c- b+ G- t
LOGGER.info("--getString--object empty");
3 {- d0 T) d7 t1 u! o
}
9 j$ L* j) u7 R) P" x% L+ m
}
. |- z2 r' O3 v2 ~, Q
return value;
7 I$ {% R0 i6 D4 G, K1 ~3 Q
}
: M3 \4 o3 o. n
2.2:在redis中实现时间控制
( }: h* h" O9 L9 `
& h! L- R- }& P- p6 q
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
5 z, i$ l+ o& [. [. ^& [
' y- g. w8 k9 F# |
import com.youjia.orders.redis.RedisManager;
3 F& j9 i* G' Z' h3 y* V
import org.junit.Test;
- H3 D7 J1 s& o% t( T
import org.springframework.beans.factory.annotation.Autowired;
' H4 `; `: v% z4 t
, c5 s) W1 X2 ^' m2 b0 }
import java.util.Objects;
2 D6 s- F( g W
2 ]7 y( v* |) o7 g2 E
/**
+ `& w- W+ X, B! i
* @Auther: Yrion
/ I( f8 K: J' b
* @Date: 2019-01-11 23:36
" {- ?( l9 q8 V
*/
, a2 Q0 ~& Q4 B5 h
! |+ G* E- N* f' q* u( T
public class RedisTest extends OrderProviderApplicationTests {
. \ }9 D# ?% d+ t, ~* y
/ `8 ^! F) B& x+ q1 h
@Autowired
; R9 N d# ^9 O+ d" A1 x
private RedisManager redisManager;
4 h# t) q6 P/ \8 K
0 c! H1 ^/ O- n7 n* s0 E4 X
@Test
$ t! [, t- s9 p! M% z
public void test() {
# n% U3 T, T/ W: v5 A) F
controlTime("10000001", 10L);
) I* p* f( P3 D
}
+ P) H# `4 K8 Q; l3 K
; ]" W, L: S- {2 i3 j8 j3 W
public void controlTime(String requestId, Long timeOut) {
7 {' M& F% m! @0 @* f
_5 B5 G! M H, n. v! J
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
) ^4 g0 Q) U0 b5 u* J& u+ [5 {- o+ q
return;
+ ~3 q6 N1 o0 ]0 l4 m
}
+ }8 \$ e* J3 H& o. i" A
//something code
8 V1 W9 Z) w* K0 I( z) ?. d
final String value = "value";
$ j0 W1 Q3 F5 h" S
redisManager.setObject(requestId, value, timeOut);
5 C) Y7 q7 M5 l P" V% Z
final long startTime = System.currentTimeMillis();
. T+ K2 |/ N5 B. o( [8 v4 Z2 w& u2 {$ V
System.out.println("开始控制时间");
5 a5 |# U7 c4 h5 s3 Z( e
//start
% E* l; g+ G Y- `
for (; ; ) {
, C- S6 I0 N: @8 R4 e; v9 a" \
if (Objects.isNull(redisManager.getObject(requestId))) {
# R$ t3 N3 z* y& y% z. f
break;
3 g. O% c) f; A& J
}
# ^, k* k: U) g6 u( `; y
}
$ m8 C+ n8 F/ j, d# F
final long endTime = System.currentTimeMillis();
, G8 q ]3 v6 z# U3 p+ |- \
6 s/ F1 l1 ^- P: H+ e- |
final long useTime = endTime - startTime;
: O0 R+ @- J! F6 q& V$ L
' q2 Z; N# d" W7 R2 C5 A) _0 M
System.out.println("一共耗费时间:" + useTime);
: |% G: I* E& A4 l
}
0 f* B3 r; |/ H, q* s
}
5 ~5 ]( h+ { [) D: m
outPut:
5 r1 U! k2 K9 Z7 [
" i5 r# Q* P( s0 ]: n9 I9 ?
开始控制时间
8 L) O5 J1 q: i: x
一共耗费时间:10042
3 ]% `/ p$ a4 e4 n3 m( O
三:总结
7 m* u8 V% D" x9 n
9 `. |7 V$ [- ~) i& A
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
; J( ]2 p- [' ?3 C8 H
————————————————
: v6 d. r, e( C. ?; U
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 C$ S0 j+ v1 E0 B5 X
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
% O" n' q' r2 o) l7 t1 F/ f' D
" W5 E% R: W/ A- S+ `
) @0 U1 h9 K: @5 G" z4 p8 V
作者:
1661888967
时间:
2020-5-6 09:57
学习一下,感谢分享
" o5 Y8 Y u6 P9 c
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5