- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 565783 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174958
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
0 V5 O0 a; i8 J* T$ G' B% o
Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
# F& H6 R" }$ o
6 C6 D( c; D8 z4 x5 d6 H; D. _7 l一:时间控制的几种方案. u/ G7 G& [: H; n
% @+ N f6 |# U$ ?7 L
1.1: 从线程方面解决
+ c9 f) b+ ], P6 |( v, G# T. y) t6 V6 z! l! t" X
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
% z3 |: F% A7 b, x3 k4 n$ O
6 i, A1 \" v$ g+ R5 o) Z [8 ]- Q1.2:使用Timer' ?% R }4 H2 Y
+ _+ Z6 W- \/ W6 h& l2 q/ ~. I8 [/ E
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
% g6 _% X* {1 ?. x x
/ m4 g* c& X( N3 ^; Ypublic class TimmerTest {
! p' ^0 n: P! W) d T, {6 v /**% _- o( O- T3 q/ g, n9 `
* 测试方法
/ X: M2 ^, h+ C# J) |7 g2 t */
7 ? g: b' G) G/ ^& B, \" B* F public void test() {
) C- P2 U; x( }/ J$ V- s' p' J4 o- E; S Timer timer = new Timer();/ X/ @# \( |4 M: e; \% Z
timer.schedule(new MyTask(), 800);" e0 w, v. G8 E
}0 U1 ?! j4 _. F; Z
4 R7 w1 I; H! ~6 ~( b public class MyTask extends TimerTask {" |. X2 h$ M. r" z3 X- S; \% b
9 W& t. ?4 X- t1 N* _& w
/**# h/ p$ `- O% x( e V* Q
* 运行方法
& U( h- f8 d4 n! |% r1 R, H */
' O" B! X; Q2 b4 O- t3 ~- F& U @Override& V* P2 ?/ L# C; Q% X5 y$ {+ x
public void run() {
! k0 ?2 T0 q- L5 m4 L2 j System.out.println("输出");
, l3 B6 |( b6 p+ i5 ~* s* \* x }
9 O3 n8 ?" U/ t. n* b, V }
% N- e8 D9 L$ u" c) Z1 x}. C. t( C# Z' L3 p: `" \7 b
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
1 C! W5 \0 Z7 s r" Q R. q) p3 I) S6 u3 J
1.3:redis延时
% C/ o- R& {: m3 ~
- i- v B2 Q4 S. r5 e' U* p在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:5 r$ @, h( i+ E2 x$ v4 i) @
1 d( i# T" P6 M) G
6 \! D2 U2 g: D3 U" n% {1 a; \8 I% X$ t
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:, c- @) F2 F: j/ r5 q4 h& |/ l( D
6 |: e j- ^1 m: x, D5 b1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制8 E& w* s- b4 Z, ^! y
4 r0 q; P2 j8 O- r& E0 v2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现3 B* o$ y; q* n8 J* Z7 s
4 W3 l; D+ q; Y" r0 j
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。1 m: g6 E5 a6 t3 g+ |; Y* I7 ^ e
8 y6 `" f. ]' D. ~3 M& ?
二:redis; v4 @5 |/ S1 C( }$ c' }
( S" R/ Y% @1 m( T! j3 p. I3 [# z# r
2.1:maven中引入redis' s+ u- r k" ~+ h8 X% v' w7 ^
: X/ l/ |, X3 H3 F E! R引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。4 t0 Z( Z; n$ h; ~ V9 N1 {" t; J
! h# [% j) g7 b. B- r, R
<dependency>
% M( K9 F) @ Q; I" c# J& d <groupId>org.springframework.boot</groupId>
; d9 T% O9 q& K$ | u <artifactId>spring-boot-starter-data-redis</artifactId>
7 ?* G1 C+ e* K# X <exclusions>: B" U ^0 a" W& d. ?1 v% Z& L
<exclusion>
8 {/ {# ^5 u: z+ G+ [; Y <groupId>io.lettuce</groupId>
: m) {) q) q" r, m- `6 g; R8 L8 y <artifactId>lettuce-core</artifactId>3 t7 Z% d; T$ k
</exclusion>/ _3 L/ y) `8 Q! k
</exclusions>2 H$ Z( z+ Y( k# i
</dependency>4 B, I1 F0 z4 ^0 F {0 Y* Z1 [# j
<dependency>, [! A4 o1 z2 i0 ?" Y
<groupId>redis.clients</groupId> K0 O$ K6 K3 f8 p5 h1 c
<artifactId>jedis</artifactId>
$ T$ f6 A$ q. i4 o9 E</dependency>
, Q* [' X' m* c2 o+ j6 @: _6 {2.2: 在springboot中配置redis
1 Y3 J+ q' Q! I% b2 c
2 C3 r; S+ u% h" aimport org.springframework.beans.factory.annotation.Autowired;
! b) B( G1 F( O8 d/ k: eimport org.springframework.context.annotation.Bean;8 y( D. ?& s; g1 w
import org.springframework.context.annotation.Configuration;% ?) i: @& y2 |2 Z! o Z2 |
import org.springframework.data.redis.core.RedisTemplate;" N, p, a! ~) Y6 F! w0 s
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;! ^ [7 v2 w2 l9 L. o
import org.springframework.data.redis.serializer.StringRedisSerializer;
( G z* D0 k8 v: w! a( Q
, q2 g5 U* Z/ l' @@Configuration
; {' A$ I4 V+ ^, H5 Vpublic class RedisConfig {/ i9 m8 i6 S) _5 \+ q
' h6 G* A' S1 w' R
@Autowired
$ c) x% [. C2 L3 ?9 k private RedisTemplate redisTemplate;
4 P& o& h5 y+ }
" f1 N! B' z2 x" A: ? /**; b- u* r! F+ I9 W \
* redisTemplate实例化) n9 U: a, }+ |- ~ l g; l8 X
* t; h5 z: x. `3 _2 ^
* @return
% @) J) }$ ~: d! a: u */) m$ M. h* v4 Z( o- q
@Bean
2 H$ S) D$ R( d1 { public RedisTemplate redisTemplateInit() {9 ^4 d0 K R6 z- s4 Y6 Q6 m4 d
//设置序列化Key的实例化对象
7 I* {( L0 Y1 e' Y* i% N& g redisTemplate.setKeySerializer(new StringRedisSerializer());
1 M$ J2 v' _/ B. X0 v M) f //设置序列化Value的实例化对象
1 N4 x7 n4 ?, _0 C# s redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
1 ~/ L3 F" y8 @ return redisTemplate;
/ N2 |. X' i9 l }* P) G1 A, J: H
* d' f' y r1 w3 `" A6 [% R1 g}3 {7 X# Q4 E" `/ f' P; [
2.2:redisTemplate模板工具类5 w( E$ `7 W4 l+ L+ \
; p7 e9 {; }- B+ E# B& @@Component* o/ |2 c' N4 r& S
public class RedisManager {
- l- h5 `2 H& e: V. ^" m3 d( e H* _. Y4 a; d
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);1 e8 \7 ]0 I/ r. l/ L% L% D
9 h" C& S* \% _/ m ]+ Q @Autowired2 X8 c% T) ]7 m1 O& N$ Q
private RedisTemplate redisTemplate;' G0 w9 N( i- `4 K$ E/ ~* [8 P
. r2 C7 Z: }# N* P9 m7 c2 u% L* R2 { /**
I1 V0 ?% B! ~4 {2 s2 ]/ b6 c * 设置对象8 z' z+ O8 F! t& c5 Y
*' h6 |0 H/ Y7 f0 U. z/ O
* @param key key
# ]8 l3 ]4 U8 `) ]$ q" x6 N * @param value value值
% }' E( \# R, i2 D1 d# \ * @param <T> 返回值泛型: T+ D. v/ O( `& ]" t! c
* @return 正确的值:<T> 错误的值:null" m7 E$ v( Q3 T
*/
$ ~' I+ ]/ Z' f- N @SuppressWarnings("unchecked")* I8 I- K: N3 A; w& W
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
, J4 b( G+ A5 j# S5 b& T( ] final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 k9 S+ J- d5 p: x N4 y
operation.set(key, value);
( W* ?7 a. C; u, Q return operation;$ {4 C4 T" I& j
}
* y0 X% w' o3 [. r
/ Z8 B; U6 A0 H. A /**4 `* q7 O2 ?; l6 {& G
* 设置对象及失效时间 (单位:秒)5 Y, D/ E; Y* C7 m* e
*
. l- @! f0 q6 l+ @ * @param key key
+ Y" }' q$ Y2 P' s9 `9 f+ `( ?4 | * @param value value值
: e9 O: M6 W c * @param <T> 返回值泛型, B+ m. f0 v5 ]
* @param time 秒值: N* S; V0 g& R/ F1 X8 p7 U
* @return 正确的值:<T> 错误的值:null
& H7 e6 H! G0 Z7 z */( i% H" F. [8 A4 H! F
@SuppressWarnings("unchecked")6 U- s$ Z) I! j% k6 s3 i
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {( _! d5 D! o; ?4 y, w1 s7 z
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
+ G- H/ A5 B: n6 |" Z. w/ f" q operation.set(key, value, time, TimeUnit.SECONDS);. U& m) U; `. \& X9 E3 z3 V
return operation;
% H% }; ^! \* P& T }( |0 K7 q' z4 n/ y1 w
7 _, ?/ \( J9 \9 h* {1 a+ n
/ F; K, A; E+ W7 ]( {9 g /**
; f! u% \/ d. W: j3 r0 Z * 设置对象及失效时间(单位:毫秒)$ Y# |- Y7 Z' {4 v. n+ V
*) o+ B X. T' |4 T
* @param key key
; U9 g0 g( X* I* a3 g1 K( q * @param value value值
- S3 w) g) A9 G% L2 u * @param <T> 返回值泛型
3 m5 o# F" P. ?+ O& |1 d * @param time 秒值8 ]% Q6 S( J. s* O4 \- v
* @return 正确的值:<T> 错误的值:null' v6 G# v6 [$ o! `. {2 ]& X7 l
*/
; M$ V! J9 _$ B& B% R. D9 C6 k1 ~ @SuppressWarnings("unchecked")
" w2 i9 V9 q# M5 B+ [) H public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
; u Z2 E6 \8 _# W) s/ U final ValueOperations<String, T> operation = redisTemplate.opsForValue();
: S" ]/ o$ M$ Z operation.set(key, value, time, TimeUnit.MILLISECONDS);0 P# A8 ~+ j! J0 a; }6 v7 ~
return operation;5 R: D4 P1 p. n4 ~" ^
}
& b( ~) @6 [3 _* K
; @5 [8 S9 K! J, [: ?5 Q /**) d* m: I8 ]: v' H6 N5 l% S0 s
* 获取对象
4 a4 a/ E/ u: J" I/ T6 _9 U' d7 r *5 q! _0 Z9 P }5 [5 z1 }9 _
* @param key 键
2 {1 n3 M1 _* J8 ~) V4 I' C2 _, n * @return 正确的值:Object值对象<br>
4 j- n( b) X. A * 错误的值:null1 @/ z* T! h2 E2 _! |5 h# M1 I
*/* B0 `6 }, ^' k' q
@SuppressWarnings("unchecked")% Q1 f# V1 _8 O t
public Object getObject(final String key) {
" M3 }: o- o: `: t+ M- N' d final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
0 s# g* E, W9 } V6 d; h+ p if (valueOperations == null || !redisTemplate.hasKey(key)) {) ~# T9 b5 |0 A+ |) L" H
return null;- t' p9 p% h, a: J) F' f
}: n- b# Q9 k3 s1 O0 r
final Object object = valueOperations.get(key);
1 q% M3 T1 M/ z# C/ u7 I& g8 u return object;
* P* D+ G9 @! z! z# k" P) k0 Z5 c# Z }- n1 M( w7 e8 M% G+ Q
- k) b. v1 W& D6 p* U D# j /**
/ S, ~2 V, V9 H/ R * 从缓存中获取string值
1 @, O9 c* H& c) \ * `& z1 Y7 Y- w" Z, O0 x" F
* @param key' U( a b# U# {, O! G% r
* @return*/
1 s! L, N) S3 N: I5 u7 [ @SuppressWarnings("unchecked")8 K0 Z# k# S2 z
public String getString(final String key) {
- T7 U1 X# v; ?. ^0 h String value = "";' O1 H1 A, ^: ^! a2 a; E
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();- ~" |( { f5 P& M5 S" D) w2 Y
if (valueOperations != null && redisTemplate.hasKey(key)) {2 M7 g4 y- F" ~3 V8 I; I
final Object object = valueOperations.get(key);
2 [. N# S+ R4 z1 } z. B if (null != object) {2 H: t) O4 [+ P+ S
LOGGER.info("--getString--object not empty");7 I" \$ T3 m# c7 G" x
value = object.toString();4 K& `7 m" c+ T. x' [0 V4 A' ?" o' P
} else {% b2 e+ w$ Q- G
LOGGER.info("--getString--object empty");
/ y& J5 Y Z: ]8 y' o }0 a2 w3 C' U6 Z% s, r4 z
}
! L( L, n: e. Y% U$ h) S return value;4 I; [2 d ~! z7 O" z+ q
}: ]- q( U. P& z7 b% I ]) ~
2.2:在redis中实现时间控制4 x* F5 ?& `. L6 _; T Z
! i: n" Z# n. |# O4 w# e) s$ d {
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。2 y, {9 e6 L' G% a5 ]
+ d, v4 K, v9 y# D: E
import com.youjia.orders.redis.RedisManager;) n) ~' ~- H3 j& i: P
import org.junit.Test;# W, U. Z# I2 [$ p2 `" y. x( s. W- |# W
import org.springframework.beans.factory.annotation.Autowired;
+ ]) H, q% ?/ ?3 J5 K d. o8 d8 D8 h/ o7 m
import java.util.Objects;
, ?1 p9 U( x' t/ F9 ]. M7 Y4 L7 E) u( x* I2 B, h7 x
/**
5 Q4 C# ~7 z) n3 F7 v4 O * @Auther: Yrion. B/ ?. ~2 W2 l4 [* \- F- L: n
* @Date: 2019-01-11 23:36
; s- Q9 s# H, c2 g3 U0 P6 C$ Z */, ?. e7 Q9 e. x1 G# t
G1 L& a7 \( z* P! spublic class RedisTest extends OrderProviderApplicationTests {) d, L; m' j/ r2 \( T: }' N
) G7 W4 u; t* \ ^0 @ @Autowired9 \1 [1 R$ @5 T9 F
private RedisManager redisManager;
" \6 {/ M4 q- O! d" v: P
+ J% {/ F& Y$ h$ g. y9 A @Test! R/ ]* t! u4 b# L) z/ n0 y
public void test() {
8 w! } Y$ d6 p5 u$ P controlTime("10000001", 10L);
% B0 h9 Q4 A* Z/ W& f }
. \+ W# z2 [9 b" R; V- @+ A9 p/ ?; W! _* ]$ J- v4 \# e
public void controlTime(String requestId, Long timeOut) {% i6 a8 b% o, ^# C
! z& G, U, q, N% h# V# u
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {6 J+ t+ Q: {+ N! n, _- m) l
return;+ S& c$ }! [! I1 v
}6 N9 Q0 ?! l" @
//something code
6 y1 E9 J0 I# f! @ final String value = "value";
$ O0 `4 ]! L5 D6 L) P2 _+ z redisManager.setObject(requestId, value, timeOut);) w6 ]+ x6 T4 e2 ~
final long startTime = System.currentTimeMillis();
( Y& }* X& `& ^. ?/ } System.out.println("开始控制时间");* e* L. [: g L$ c3 p
//start, R @ l8 _/ f- \! j
for (; ; ) {7 ^" M9 n6 G4 t x
if (Objects.isNull(redisManager.getObject(requestId))) {+ Q$ m* G3 j. t% o6 E x5 }! c( [ g
break;$ m X1 }2 b* Q8 z
}2 h3 {" i" J, A7 Z
}* ?) m: v* _1 F/ n- U8 h \
final long endTime = System.currentTimeMillis();
# j$ z: ^# g! b* U2 W% j
5 j. r# ^/ _$ U* y0 ?+ j, ]0 r: } final long useTime = endTime - startTime;" C" S1 D; {9 L$ H9 x n/ ]
3 c; Z+ [% P6 p: ?% f P System.out.println("一共耗费时间:" + useTime);. J$ p A# x7 O- V4 B3 U6 o' g- p
}
' ?& W- K( P( w& a}
* C X( \& d2 M5 R7 ]outPut:
$ B7 _) [; Y3 L8 e
- J( U& O; p0 ~; N! V开始控制时间
9 f# q- c) o- k5 p8 u) y1 h一共耗费时间:10042
|" U( b8 [% W6 W3 \三:总结* v2 X; \, k- l2 d! @: W
/ e" F! Y5 J7 X' V) j8 @
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!0 I' S9 w% @! W' O: A
————————————————- w3 l9 m5 X, T- a& ]
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。+ R8 @# I0 w' J
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933254 ^7 ^) F( F8 ]+ N
Z" B& v! i4 G* y8 b
) U6 H+ I8 u& ` |
zan
|