在线时间 1630 小时 最后登录 2024-1-29 注册时间 2017-5-16 听众数 82 收听数 1 能力 120 分 体力 563399 点 威望 12 点 阅读权限 255 积分 174243 相册 1 日志 0 记录 0 帖子 5313 主题 5273 精华 3 分享 0 好友 163
TA的每日心情 开心 2021-8-11 17:59
签到天数: 17 天
[LV.4]偶尔看看III
网络挑战赛参赛者
网络挑战赛参赛者
自我介绍 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
群组 : 2018美赛大象算法课程
群组 : 2018美赛护航培训课程
群组 : 2019年 数学中国站长建
群组 : 2019年数据分析师课程
群组 : 2018年大象老师国赛优
8 T" u" R( _5 j p; D' g1 S Java如何优雅的实现时间控制 前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
8 t# B; M( U6 S) |
2 {8 L( a0 w: n- Y) I$ ~+ Q! v 一:时间控制的几种方案
1 \% Y! w& \' T% V! h 7 X, s! d" A) d+ [- N+ w; e5 Y5 ]
1.1: 从线程方面解决
& N6 K2 O/ j! o+ R' J* A4 W( W
+ ?% y# O4 M' a* L) N 最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。$ [" {7 G. [; R7 i7 P, p$ {
. X' o5 U9 H& m: R 1.2:使用Timer9 ?8 U. x. L: D7 L
3 j) c* j* }- I* `$ _2 K' t
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:- z" d7 M8 T: A! d) Q; f! }
+ w U- x, f* w: G) g public class TimmerTest {5 E6 e$ F: r2 t/ O' I+ w' K
/**2 ^4 S; G* p* p+ y/ N
* 测试方法
) W* `* ^' i% Y5 Q */
( A3 z2 T5 ], v) ]2 T6 j3 F8 ? public void test() {
2 d( P/ O& M7 A) Y* [" _ o Timer timer = new Timer();# ^8 b; ^+ a4 ~
timer.schedule(new MyTask(), 800);
( V/ U( m( l9 @, l }: s: U* h, c/ P3 h- q
) @" c8 ~! _* V1 s public class MyTask extends TimerTask {
( C0 w0 u' y6 ^- G; x9 Z c/ y2 z . {% H F% m3 G# [
/*** _4 J/ n/ t Y E0 G
* 运行方法
: B/ k ]' @" C S: x w */7 M; K/ ^0 Y9 M
@Override
! R! [- p, v4 I+ o9 f public void run() {. T9 I6 r6 h* ^
System.out.println("输出");
( y2 }- H$ A6 K- ?6 ?4 x3 n. z }6 }) d4 |+ h g/ d- e
}5 {( }8 {8 g( m( t
}
3 \0 k1 g0 H M 这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
( e& T0 l, T' N' _' w$ x' F! |8 i y1 l! W# ?* g) H2 E$ X# O1 u* I H' T
1.3:redis延时) `- s: D5 |4 A2 o C8 M* y
( y7 F }; ?3 C/ h. A3 B( N
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下: y# p" P" \- i4 @( @2 P$ _) t: y5 N
3 X6 @2 k5 T* f ( T: e0 E& R4 ^7 J9 G5 w+ ?3 Y; P3 g
0 P5 G P0 \+ d- ?
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点: x# E& u, b. S& H
9 P0 E1 P5 d- i6 }: C/ P5 b$ [2 V; |/ m9 N 1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
: G' Q4 X% a8 c/ O
$ p6 h- Q+ P- _& N- y 2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
/ h* ~$ L% P0 C6 z9 K7 F 0 X$ I1 w% V: \
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。4 \ u/ c$ w& m$ O: Z: ^
+ t& s" l( U! O0 h! H, D 二:redis7 k4 H' z0 |# h- W Z+ c: x# a8 Y
1 U8 T5 m! [) D 2.1:maven中引入redis' n7 B5 r9 O6 @/ Q! S/ X% |
/ d1 d. F) O1 X$ } 引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
$ H! [# f5 _- {9 ~( M Y
0 r) d. P S( I, r) t9 g5 i <dependency>
5 S! D* `1 @0 I) ~8 R <groupId>org.springframework.boot</groupId>
: g8 e( B: m. a; ] <artifactId>spring-boot-starter-data-redis</artifactId>
! m7 m" I9 [2 x9 q9 \' X2 ` <exclusions>
! i5 b; J' f( W( A6 w s. B+ W6 { <exclusion>
7 }4 f! I) B9 h <groupId>io.lettuce</groupId>4 Z2 a4 J4 k8 U# p: r- |
<artifactId>lettuce-core</artifactId>
8 o! N8 s2 z+ p6 S6 ]. n! ] </exclusion>) R; \" N& h# X/ n/ c8 E; b: B4 y0 q# n
</exclusions>
4 X& ]# r4 w; G) }0 d7 d2 t </dependency>. Z- ], S/ _0 t: h& S
<dependency># e" I1 I6 a/ r: S. R
<groupId>redis.clients</groupId>
/ M) @4 R4 ~, ]7 Y. p <artifactId>jedis</artifactId>
$ f, f l3 s6 ~( X: z( n7 c2 T </dependency>1 g7 D& N$ Z m; P2 f. ^2 n
2.2: 在springboot中配置redis! n, B/ \8 z$ G4 X
& }4 O/ v8 C# S: R+ n& E& m- t
import org.springframework.beans.factory.annotation.Autowired;% G( i) ?% W1 }4 f
import org.springframework.context.annotation.Bean;4 C0 o T% c2 d2 C' i3 j
import org.springframework.context.annotation.Configuration;
$ J: z- w9 N" P, W9 N import org.springframework.data.redis.core.RedisTemplate;" l$ }0 n! @9 L# N
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;5 h1 j: |1 M: {+ G( D# T' F0 p
import org.springframework.data.redis.serializer.StringRedisSerializer;& @, [: V$ z& l' d6 N, Q- G4 z) l
1 D# p4 v4 c9 P2 K* ^ @Configuration
$ C- C/ `- a: U7 w9 S: Y2 s6 U% j% q public class RedisConfig {
+ `8 P6 r1 u% p4 R" S8 t# P) V9 z4 h- r ! }, [6 |# P) |% B: A
@Autowired
J/ o6 P9 _4 `7 q' s' S private RedisTemplate redisTemplate;
# J% m& ~4 n1 F5 D
8 T) i& T- l5 M2 \/ P, z0 Y /**
$ F1 d! R& h3 F3 C" _ * redisTemplate实例化
! U" U7 K, `% a; b* L! `1 S8 h *
9 a# c0 l% e; O. \. _. |1 f * @return5 c3 a3 E% A% {$ v! z+ ^& q
*/
: m. x% @, t* L @Bean% V5 w/ d% Q4 {. d X
public RedisTemplate redisTemplateInit() {
$ O/ O. d+ l" U( m //设置序列化Key的实例化对象& t& \' ?0 N) w2 E
redisTemplate.setKeySerializer(new StringRedisSerializer());% z5 H6 n# W' z5 G' K
//设置序列化Value的实例化对象
5 q2 T5 y P) ? redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
/ P& }: Y6 X8 T4 O return redisTemplate;
1 F6 a: C: |; j: C }
' i, L2 ?7 Q7 P$ s0 ]1 Q
9 X: v" U7 ?3 ~2 P& c; K$ R0 }+ R4 ^# [1 x }
( u/ V, }" B5 G; b) i g4 N 2.2:redisTemplate模板工具类6 Q9 d0 C% a+ e6 C
8 q6 g! \3 u8 @
@Component% J" z l2 M8 \% n# G% S6 G
public class RedisManager {
8 }9 n* f& r0 { u5 x) T
6 G8 g8 I$ b6 M* | private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
. x, Y6 l7 r2 C! X5 b7 q* k3 w . n' W+ r- r, s2 R/ {+ T
@Autowired3 x0 H3 w, ^5 a" H" q. k8 u
private RedisTemplate redisTemplate;
0 L3 n/ i$ A) {+ r
( c! p5 S$ A# d1 x& ?. O /**7 Y% a' y4 I% Z* {
* 设置对象
5 s6 a C2 g. k0 Q0 O *( t& W8 X# S+ }. ?. _; V
* @param key key
- w" N+ _3 ~' X6 O; ^ * @param value value值
& `+ V* {* M$ Y * @param <T> 返回值泛型
% A' H# U2 n1 k0 r * @return 正确的值:<T> 错误的值:null
8 M6 r% M+ _$ B+ e+ P* \ ? */
! u9 F$ ?% m- l @SuppressWarnings("unchecked")2 \1 m0 |: y7 G- _1 z- s Q
public <T> ValueOperations<String, T> setObject(final String key, final T value) {4 K4 I' C9 N. [! k
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
0 S$ |' J/ {) }" }+ J+ y' v5 W operation.set(key, value);& q3 e) { ]" k O: O7 i
return operation; n" M/ B/ h3 p6 a$ D- f6 V/ {+ J+ w
}/ w& P D/ [7 I0 v0 U- c$ }) f
( V9 ^3 o2 l6 j9 ]; ? /**/ f" K* O- g5 ~" h# Y; d n, T! G+ c3 a
* 设置对象及失效时间 (单位:秒): V9 M0 @- x6 A h" v
*
. k) g \/ e# S: ^, G: K * @param key key
9 t2 Y, Z* G3 Z) v * @param value value值
9 B. H2 t2 {( b, H. F, z# }4 `) E * @param <T> 返回值泛型& W. b; _# A( ^
* @param time 秒值
6 S8 T+ U5 U8 S. d; D0 J2 b * @return 正确的值:<T> 错误的值:null
% y8 w1 k8 K- G5 @7 |, n */
2 f9 F9 a, A& k F$ p @SuppressWarnings("unchecked")
& B2 n7 }6 _2 v, C public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {1 B: `2 V! w1 J7 |, U7 E
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
; h1 { k! Z; J2 ?) E4 [. E% E- h operation.set(key, value, time, TimeUnit.SECONDS);
/ a5 ^7 K% M L* K& t return operation;
# o8 G4 j; q) } }7 f) }7 W: L9 ?* S/ o. O6 v
4 _7 F s" u m9 t' X" ?
/ F* s: K9 z/ A2 ?- }& `
/**
$ Y1 B2 f) T& G2 S * 设置对象及失效时间(单位:毫秒)' v |# E$ `, s3 A- w, h6 C
* B2 z/ z) |. ~: \# v- V+ u, J
* @param key key, M `$ W7 T f" T7 q
* @param value value值1 D2 R" L7 `" m- i
* @param <T> 返回值泛型
8 P( g7 | P: A * @param time 秒值
+ d2 `* c! O" C4 e( Y% G * @return 正确的值:<T> 错误的值:null
8 W# y! k+ h2 l" u$ | */
$ V! r* k1 s$ ` @SuppressWarnings("unchecked")8 [/ b6 w* h# N7 T2 @2 i
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
. B1 U" b/ K! a& W final ValueOperations<String, T> operation = redisTemplate.opsForValue();: n: n3 |! T8 }/ g- r
operation.set(key, value, time, TimeUnit.MILLISECONDS);
' z2 M8 x/ J+ |+ V% v" J return operation;
/ w+ m& x) \6 B7 j' C+ l) z }+ }3 w% w8 f* l. T8 E( D0 x- c
0 s# R) K6 W$ S
/**8 B, p8 I# C* h% D' P
* 获取对象7 t; H F+ y4 c H+ q
*
' b! \, Z+ e/ o% r) `, ~$ S * @param key 键
4 K9 {0 r& J. w' I: t* a * @return 正确的值:Object值对象<br>
; X* m6 t% I# s! d- ^# V# C * 错误的值:null6 p& b: F% k( w V& k: f
*/
- s- F6 _8 T4 ^ s4 G1 N9 y1 `: M+ J @SuppressWarnings("unchecked")
7 y& E @1 a# ~) n; I2 X public Object getObject(final String key) {$ O( a ]0 p$ N5 }+ V; p8 V
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
, _2 f5 ]4 W4 K6 t" }+ c7 y if (valueOperations == null || !redisTemplate.hasKey(key)) {$ S- V* i; `0 C( a! ^
return null;
* k5 h) e1 A3 M, s; j( m; N }
7 [5 s' ~% D8 l' f, K# Z final Object object = valueOperations.get(key);6 n3 `- O+ w. N
return object;
- D1 q: g; F0 Q }8 R: h. x7 a/ J8 A, c
8 V2 L0 ~* E! ^& ?$ k+ q /**
1 _& X2 M! q( u * 从缓存中获取string值6 A5 X; R4 c+ }( O: X' z; \
*
# _9 w9 D8 B& w; u * @param key4 `# c7 K' l+ M0 i& H, l3 d x
* @return*/% }9 y1 x; Y% j. [8 t% i* R& ~4 E
@SuppressWarnings("unchecked")0 A& M$ X: n {1 w3 I# A7 R3 c
public String getString(final String key) {
5 {0 w$ w) W/ B String value = "";% R9 U9 x2 a$ n$ E0 N
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();8 J. w* m& k- f4 N
if (valueOperations != null && redisTemplate.hasKey(key)) {
& L. \) ^$ E; ^6 o* l% ?& B final Object object = valueOperations.get(key);
& [. C+ m1 o- b. B) i; O( l% z3 l# T if (null != object) {
t& @7 ^% w8 p* U( E1 J& t$ K2 V LOGGER.info("--getString--object not empty");% T6 Q- Y$ Z5 J! ]' g0 w2 l
value = object.toString();6 n" ]* Z D( k( U2 [6 ^
} else { \) i9 Y7 N7 J% t# z; J
LOGGER.info("--getString--object empty");) A; T2 W0 W0 k+ C0 l
}
! Q* i* \9 g3 r- | {& d [ }, _6 n8 t9 P8 Q' A
return value;
6 Y2 l- q, d/ Q& N( o& d }* _% k/ W; z; d) m/ p
2.2:在redis中实现时间控制
8 Z4 ]) W+ S4 C2 A1 E Q6 b3 w" `7 @
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。$ Z* ~' }8 V2 J
! p+ Y! E2 x( }- ^5 c import com.youjia.orders.redis.RedisManager;$ _! _) Y- X) D4 b/ e/ Q- M! m- y8 ^- G
import org.junit.Test;
- w x; i' D0 ?/ u" G/ t9 `- P import org.springframework.beans.factory.annotation.Autowired;
( ?% ^/ K& {# V' I/ \2 Q ) O1 }8 ~0 v" x+ \; {
import java.util.Objects;
6 m% t y) X1 D* W+ ~5 d ( F8 H7 ?8 P0 T, u
/**4 w' p! b1 C+ z( I- b3 u
* @Auther: Yrion. E4 [' Y$ }. F: x5 ]
* @Date: 2019-01-11 23:36
. p& Q* _7 V' k5 ~( {. n */3 _/ l4 C X8 Z& [" @
2 F/ h8 {5 N9 ?, \% W+ q" |: k1 k public class RedisTest extends OrderProviderApplicationTests {+ A0 e$ A h* B o7 S5 ]0 h8 P
5 h( M7 B; W) m# q6 t) U @Autowired
. ~ \' x G6 Z% c O private RedisManager redisManager;2 s8 D) K h6 |( [( e! p3 W. N
( E- R9 \0 E' i
@Test
4 h7 z' u4 t( i+ j4 H# q; Q public void test() {9 D3 y. o$ w, r; X& V# ]- S
controlTime("10000001", 10L);$ b9 P1 O8 Q4 r
}- |& b8 n3 F1 W. q7 A
4 ?* E! v, K1 X
public void controlTime(String requestId, Long timeOut) {2 Z; e' b$ x8 @
u0 o5 N% e* ]* S" B" x4 ^3 p if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {) X2 M. h2 m/ |
return;
8 ?1 B0 U; H ?% x }# u6 r5 ]- r4 w3 @! k- u6 R1 z
//something code
% \8 v7 g) j3 [7 Y! H final String value = "value";2 t& N. f: W) j6 J! U) ?7 c; V
redisManager.setObject(requestId, value, timeOut);$ l2 w, F; J [0 U0 q4 q
final long startTime = System.currentTimeMillis();* W3 D8 q' E- v! R' n& P
System.out.println("开始控制时间");
' E0 k7 J* o J, z. \, `6 _: e: } //start
3 m3 R: |" N+ x7 k for (; ; ) {" o$ M7 G/ e9 I# }! Y$ x# [
if (Objects.isNull(redisManager.getObject(requestId))) {
1 g4 p _+ Y5 r/ l break;' h1 f! M$ Q7 B9 ]
}+ h4 ^$ Q( R2 O# p# E$ q! b0 c0 v% ]* ?
}
0 O) ^4 E- a: g; N* v final long endTime = System.currentTimeMillis();. [# s, e, P' m
; m' }9 }. B, t. j final long useTime = endTime - startTime;
1 s' k4 T/ b& q) R6 _" b
! U$ z+ q' x _$ L System.out.println("一共耗费时间:" + useTime);' G' k5 w# o4 H2 i
}
: e6 \' X% q7 o8 |7 R1 _4 Z, { }1 b' u" j" u# f6 q1 q2 G- D) X4 L
outPut:6 A7 \& d/ k& F4 R* S
- y; f8 I! h6 Z& u. `
开始控制时间
& z% k7 \6 ^; o n# A/ U 一共耗费时间:10042
/ @3 K! h8 F8 y' R; K; }1 s% l 三:总结: x0 q- q9 {6 N- q) {$ x0 G
& H4 U* B6 ?: k* |
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
5 K' k( f' U, b1 C' `, X ————————————————# g& K0 n% q, _/ ^( ]+ y4 E/ e
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
; }# X# P+ v0 e, y 原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933258 z: a: J6 _$ W- T6 H
+ V7 d+ A2 }2 L% o4 [1 K9 d! c2 T
/ c. i" J8 J& U0 o3 k U% C( C& r# T
zan