- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 565542 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174885
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
' i- J. e) X5 l' pJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
5 \9 X- x1 t+ x6 V% ?2 g
) F! E* l1 R5 m/ }, a6 [一:时间控制的几种方案& k+ O' R: w% e3 L: g
, D& h) }( g! i' b0 N9 L
1.1: 从线程方面解决* W3 E& z% @1 F. S- p W
) z ^! |1 @1 I2 g8 `+ l
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
+ K2 |0 |, u. \7 F2 I% U% v U" q& R
1.2:使用Timer' d4 V8 C, H3 l/ O7 ?
8 r$ |" J$ {2 e/ E3 Q查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
1 O/ b: |, D7 j1 V6 C5 [" c+ i) w3 I: T6 i3 v5 l) U
public class TimmerTest {' k# e3 y5 n! ~& }" @
/**$ l" \3 l3 M9 k" o/ K8 Q
* 测试方法
0 W3 g. b' s1 W' k9 S */
& N* K4 V! ~& N public void test() {
! d0 o6 V6 G0 u2 r) W' C, P- Z Timer timer = new Timer();7 T# R |' t) m1 p. ]) d2 |: p
timer.schedule(new MyTask(), 800);
4 I+ e, N4 p# o }5 B$ Y; U' k& F* p9 _
: ^1 g- r; P2 \: c- Y7 A1 Z public class MyTask extends TimerTask {1 V/ n% x1 L+ \- C( g
1 @' G9 b* v* ^- ~8 U! B
/**
2 a: N5 ]4 T( _1 W * 运行方法
+ {2 k; }3 e, ^9 k4 H! N( c */- m; P* _5 \( ^
@Override" ^5 z4 A9 p- H5 W
public void run() {
, _, F: V f9 g2 c System.out.println("输出");6 B3 T6 j% x4 {( L/ `# I( Y1 L
}0 u- B- A. Z. n; \, u
}8 @! ]9 L. V+ k4 U# ?- g( h' q8 \
}0 b* l+ C, c1 H" d4 A
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
% h+ A/ o/ @; W: g( W, W$ X0 @" S6 \
1 F* E3 O0 N, d! O, W2 k# O1.3:redis延时
" P) l* w w: g/ r+ B( [# F' u4 W# i$ k9 \6 j N
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:' Z% A# t2 f* S' s
7 O' q+ e% y) q; z
& {( ^0 }2 |7 F( v9 Q1 n2 J7 G, P3 N! b& N: ~; n* c, t' L
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
6 X. T' m) z. i+ o/ M- i3 T4 C5 x" T1 O) T [0 ~
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制& x9 j/ H0 E2 C. l4 ~ @1 @* `1 |
: E7 x& f0 v- j2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现9 G9 n6 r6 H* `& K: O+ m( b
/ v9 _( s/ j/ P; E# t3:简单,真正的代码实现起来只有很少,下面会给出代码示范。8 _8 j, m" W8 I7 \7 D c
$ _; B$ c& n. ^) D. j
二:redis
: ^* T$ G7 B, y4 b7 u2 z) T3 [4 y- p/ B% u& E) \/ P/ s
2.1:maven中引入redis% x' h% E8 _5 N+ ~) K$ j2 y
# d* w: N& y, S& S4 M) F. V# H引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
# d2 U6 D5 q0 V/ }; N4 F/ _
% b5 F J: g9 N g( y<dependency>
; q2 v# y, s9 R2 j3 V8 I <groupId>org.springframework.boot</groupId>3 [8 C6 r% f8 T. X, ?' j# X
<artifactId>spring-boot-starter-data-redis</artifactId>
3 x! v. K! q6 _; L/ ^( j <exclusions>$ Y( @ \# h) q
<exclusion>: x+ L- B( C% y
<groupId>io.lettuce</groupId>1 p7 \( H/ w. L. ]; r' d9 F
<artifactId>lettuce-core</artifactId>
/ d7 ]! ? I8 i+ r R; Q: d </exclusion>
2 z |0 H. C$ x6 x J5 ^4 G </exclusions>
, h6 x# {/ ], K+ f' }</dependency>
& e' @# e' U; L1 H5 n<dependency>
, G& S2 s3 r/ u Z+ M4 h <groupId>redis.clients</groupId>* Q$ F8 ]9 J4 O
<artifactId>jedis</artifactId>
1 G7 x0 F3 g3 I0 \. U: X# J2 i</dependency>$ U7 k. g. f# a0 q2 m4 o# k/ u
2.2: 在springboot中配置redis
, V2 W1 `) k- A9 A* J# v+ t
: T( m5 X4 R- E! wimport org.springframework.beans.factory.annotation.Autowired;
$ e+ g& E6 z4 _9 B& simport org.springframework.context.annotation.Bean;0 C W L; B# y' [( ~" f! q
import org.springframework.context.annotation.Configuration;5 T8 ^( _# d9 t0 @: z" C
import org.springframework.data.redis.core.RedisTemplate;
. A1 Z9 F7 o; @9 A* p' u! }" @import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
- V* M( w* ]( b; cimport org.springframework.data.redis.serializer.StringRedisSerializer;+ q5 N o+ w! s3 y
5 j9 k3 x7 E5 a8 ~6 q& ]! S( t$ |
@Configuration* S& a1 F' M. V6 T$ t: \& r' ]
public class RedisConfig {
& s5 }# y! J3 Z8 ?. S
4 O6 ~9 D& K* p3 ]: t6 C- `9 M @Autowired
2 d% K' X* ]+ v. f" R( D$ B private RedisTemplate redisTemplate;# r0 m Y4 Q, Z9 B7 J
7 l3 i$ s# H n" J8 ? /**
% ]0 @* Y& `& R, a * redisTemplate实例化9 E8 z2 w; E6 R- ~; i; G/ }! W
*
" H1 o/ j! d7 I0 X- {0 |- v8 ] * @return
! t9 c- m& [7 w, k */
" l D& q2 F9 N" o' Y @Bean% _8 d" p( ]7 e+ Z% p- t
public RedisTemplate redisTemplateInit() {
) k0 |# k; c* Q: S7 A/ m- P G" F //设置序列化Key的实例化对象
o1 m/ `. G- g' \( T3 Q; _* v1 S redisTemplate.setKeySerializer(new StringRedisSerializer());( N/ S6 Q. D- \! B% d
//设置序列化Value的实例化对象. A- w6 B. q5 F7 a: h; Z
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());& b; Y! L( R0 _2 r6 E
return redisTemplate;* j# x/ X7 \9 d5 ? g
}) d; z9 e/ O. S1 S/ i- {$ m% ]
% ^( h- ]% u' i* k; t' a3 d4 T1 g}$ H( b# @+ j# b0 p7 f+ q$ S
2.2:redisTemplate模板工具类3 {" J$ }5 i) }6 @8 b% ~; o
7 t, X4 x" _. p. S1 D3 a5 g* E
@Component3 D9 I1 Z& N; S8 W
public class RedisManager {( j) l% D8 }; u- r% A7 O, ^6 a K$ N
7 h- Z8 d/ d4 ]3 ~
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);( h& P8 E) d+ o' V& l2 _2 c" N$ S7 j
% R. y, W5 _0 ^1 D
@Autowired! r" T+ y9 }9 w% P3 v; o
private RedisTemplate redisTemplate;" m) r0 m3 Z$ o2 L( P9 y. m
% X) q$ X0 k. r# T7 a. x) |
/**( _" a# X7 W7 Y7 ]3 p1 E
* 设置对象
) O7 w9 \4 K6 ]/ a" S3 w *2 ~' {& Y/ G' n* h8 H/ V
* @param key key2 m% { N2 c/ V9 d* B Z
* @param value value值
! u5 I: o9 A4 t# @# e * @param <T> 返回值泛型
/ b$ Y u: s4 m4 k7 a, a9 X * @return 正确的值:<T> 错误的值:null
4 H! ~5 H- p: _ ] */
/ ^# y* n4 h9 z: K W2 i0 k @SuppressWarnings("unchecked")
' ]4 R5 h$ w3 m9 R public <T> ValueOperations<String, T> setObject(final String key, final T value) {
$ I! G( ?' g3 P7 j4 l) j( z7 i0 W1 P6 Y2 j# Z final ValueOperations<String, T> operation = redisTemplate.opsForValue();
3 m9 j2 d7 Q$ T; ] k operation.set(key, value);
* T1 d1 P: r/ k/ C5 o return operation;4 j) U- @- ~$ g y2 c& N( C
}0 e' k5 E, q. ?0 u0 c! X5 X$ U
7 x3 s# n' I' v8 y# i) U' A /**
9 |. a2 u9 P% ?, Y/ T, H * 设置对象及失效时间 (单位:秒)& s+ x! f0 q. M3 X
*
, ]9 u" K: ~* n! p- `8 ~ * @param key key
: E& Q4 N+ |& c8 |8 o. f+ k * @param value value值
" M( x% @. q" T3 L) }- r5 `% a * @param <T> 返回值泛型
- `8 [8 u9 M9 [ \6 U * @param time 秒值$ V$ Y3 M- Q. g% D! j" D4 R! m5 t- A
* @return 正确的值:<T> 错误的值:null
! y; B- I# Z, B; i- K, g */
0 s3 x" G8 k2 ]8 m) q @SuppressWarnings("unchecked")" p5 C* P# f+ x/ l6 ]) |9 ~1 K
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {3 H; I5 I8 a: v& G" C1 e9 T7 N% ~9 c
final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 D! v" J5 G* M" d# d
operation.set(key, value, time, TimeUnit.SECONDS);, t8 j5 M H& j3 v3 s: `
return operation;
1 b% M* Q& V/ Q. R" P }& B; Y1 p; _. j7 _$ R% C' H9 C+ k5 C7 j" o
) U8 t4 A* d0 k A* L# L
+ J0 G; `, M. h5 `& _ /**
+ ?/ ^0 l! A1 ?: v/ ?4 H* f * 设置对象及失效时间(单位:毫秒)
7 I- r# v8 b) c* l% ~7 x$ v *( D! l: S6 X# R& g- y
* @param key key
' y& Z0 J1 @* {/ C9 J1 F$ d5 g r+ p * @param value value值
. ^5 o" y- \. _6 L1 { * @param <T> 返回值泛型, M1 l8 \+ w6 N+ \6 s
* @param time 秒值
$ R. ~8 k$ s+ q1 i$ I# m4 Y8 k * @return 正确的值:<T> 错误的值:null8 U% _: X5 s9 j* `. C3 ~
*/; g2 g3 G0 S9 K8 Y" H. k
@SuppressWarnings("unchecked")
: _8 ]* r& ]1 f# }8 q: [" Z public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {( n6 q4 S. j6 r* ]& {# v: s+ C) i
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
% _% C$ J# D8 Z) Q8 J( D8 [ ` operation.set(key, value, time, TimeUnit.MILLISECONDS);. _5 Y2 O8 R" I3 R6 i
return operation;3 H/ I/ b" z2 D4 Y- D5 @5 J
}
: G0 X7 N; O; C- b
0 w0 W3 ?) |4 U+ t /**0 |. U" w- }8 V6 @) q! U
* 获取对象
: G' m4 X2 A/ U* }$ J5 K6 n *
" w- [2 }# w/ Q3 ~$ @. X * @param key 键2 c; v; L0 A- M7 N7 B+ c% f
* @return 正确的值:Object值对象<br>! Y3 i5 `; A2 J. v) J9 C
* 错误的值:null* k# c7 q) |' X7 t
*/
9 g) R1 x0 b7 _- T' ] @SuppressWarnings("unchecked")- |( w3 w; t+ @5 T$ u1 T
public Object getObject(final String key) {
- U. r' }8 R; y# x/ n% s6 o' P final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
. H+ d3 k, w4 B7 _- ` if (valueOperations == null || !redisTemplate.hasKey(key)) {' F6 C) f& g; ]- J4 u
return null;
% O, K1 n9 o' k }' @: s4 V( M( G$ a H4 r" c
final Object object = valueOperations.get(key);4 c9 Y0 q+ c! ]! j1 t
return object;
# q8 n: `' ]2 \+ O$ T }" ^3 Y5 \$ l! r E0 F3 a+ j8 u- `, u
) P# t* o+ P" O
/**
: A3 x5 T% v! l2 j4 I * 从缓存中获取string值% E0 M0 J6 t6 X0 l F
*9 Y3 X6 ]0 f, a
* @param key* L: W1 n4 | y! |% c3 u
* @return*/
# c1 u% `0 ^/ f1 O9 }$ d @SuppressWarnings("unchecked")
) D5 X( G. N4 T5 ~/ y public String getString(final String key) {
) K" W4 C0 o0 g3 C& U; D Q String value = "";
$ I! L) u2 j, o ^: c$ `- k final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();5 u7 n$ p. P* K0 c8 p, f5 f
if (valueOperations != null && redisTemplate.hasKey(key)) {7 k1 n+ i+ [2 T" |: P# A# J1 w
final Object object = valueOperations.get(key);1 Y) p& Q) i* z" p* K' Q. |' P
if (null != object) {3 Q4 c* k. n5 d V
LOGGER.info("--getString--object not empty");% A: ^* |" D7 h* O8 z- K7 ^
value = object.toString();
7 ?) @7 b2 y. Y! d% S) a9 p } else {8 x; q7 q3 w; R
LOGGER.info("--getString--object empty");& R* K7 z/ s2 h" F' a( U h
}
! J6 q/ c9 n6 K( W) i. z( d; f* N. f; E }( R/ G3 j; T0 A7 B- H- ^
return value;# _) ?/ C N- b9 m6 Q, I
}1 A$ |. f. F4 q. X. L
2.2:在redis中实现时间控制+ @; m, [9 x2 h% n3 `: |
0 b8 ?+ e, B4 s/ ` n6 o5 v
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。% H# c! u3 c, O2 X# e+ H) K
, A! `8 l: K# s# B/ p" ~! \
import com.youjia.orders.redis.RedisManager;
8 k3 Y% y+ f( \, Rimport org.junit.Test;- h N3 f: f/ K6 I; _ l2 W. J
import org.springframework.beans.factory.annotation.Autowired;# @+ B; ~9 m9 Z& O. h1 W. k
! m6 P# U# m2 [6 b( I/ Y
import java.util.Objects;
1 N9 D9 ?" n+ F+ q% a
8 j$ w9 }3 v: ^2 U( s/**6 O! E) M9 K6 K, w% u/ q6 d
* @Auther: Yrion- q/ D7 y; H. S
* @Date: 2019-01-11 23:36
/ N. C' U4 `, K% T */
8 E5 s \5 i1 A/ k9 I# p8 K$ W8 v8 l
9 y8 u8 ^( o4 ]1 Kpublic class RedisTest extends OrderProviderApplicationTests {8 D8 v) H( j1 ~. ]+ w3 L+ O
) s8 K1 O2 s) t6 J& u2 W @Autowired# d* y+ y( N! b; w$ y' M1 ?
private RedisManager redisManager;
1 t+ @# r/ Y6 y6 F* t( Q4 {) ?1 \1 w0 B: h$ T
@Test
1 r9 d ]9 X J5 \0 C5 h. o public void test() { z9 D' }: `4 O$ g* o- z; u4 b
controlTime("10000001", 10L);
( \' C' u5 D* I5 X; C! ~3 G ?# V6 X }" G, J$ i% [+ W
/ D* n5 D2 F; i5 ^2 n public void controlTime(String requestId, Long timeOut) {
' r* H/ s B9 |" v8 B/ _
2 m1 K4 I0 n3 x2 C if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
2 Z7 |2 B& x8 U/ E8 K! b return;
) d( s4 _, y+ p* x7 o }
. n$ Q) N1 p1 Q, d6 W2 y //something code
5 _# f- Y- A% S( u+ p final String value = "value";
/ b( o" s# M) w- `" M redisManager.setObject(requestId, value, timeOut);
8 _) l% ?9 Q6 Y" k; _. d final long startTime = System.currentTimeMillis();
8 N( a, ^: P) i! B System.out.println("开始控制时间");
0 o/ o( B6 R7 `* w1 Z- { //start3 J8 ~" i7 [/ u
for (; ; ) {
$ R$ E1 p% f& Z3 ? if (Objects.isNull(redisManager.getObject(requestId))) {
4 W2 [) A5 Q' w8 Z( y B3 A; m break;! y3 @- `3 v- t
}/ l* ~: j; R3 f+ o& c# |) ]2 ]* n: S
}+ W9 F ]* w9 K0 `8 u. G e
final long endTime = System.currentTimeMillis();: q2 P8 F4 A! Y9 I, X/ e
1 q; A- ^" A4 _6 W final long useTime = endTime - startTime;
& v3 {3 I9 f3 T$ m: F+ B$ J* I) d% t
System.out.println("一共耗费时间:" + useTime);
! I1 [/ v n; N( T+ s0 ^+ r }) U+ L' p2 I! @: ]+ d. G7 \
}9 W8 J. H: o. v" X1 X& h
outPut:
6 _7 j0 }* p3 F8 `9 d7 H: R8 y) B% b6 G0 l
开始控制时间
% T A6 t" A0 ?, t一共耗费时间:100421 G; i9 Q8 c. a. k/ n4 s! `
三:总结( I. s% Y+ F% u& W# k
( |% g8 ~+ s' j$ r) s0 i" q% g本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!$ T$ x: ~* C6 N0 T: z7 `8 ]0 m
————————————————1 }# q2 C0 u4 P2 E; r: {! J) T" T
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
9 ^2 e- I6 K, b- M. M) l9 C原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933253 V* }( s. E' |3 C$ m9 h/ T3 L
8 p4 y" N, X* w( G/ }5 `
9 n7 y% \2 z- M; |7 |7 @
|
zan
|