- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 566715 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 175237
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
$ R2 |- Z& V) r% \, r4 D- c, l
Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。% c$ w! M& P& S/ D' p5 y* i7 y
9 d5 Z% d, x% l- v4 N8 g' D一:时间控制的几种方案
7 f8 u6 P' N( P6 H' U( n3 }8 q x2 U3 b: z
1.1: 从线程方面解决7 V3 w" [9 `: y9 j* U
6 Z9 E- r0 a8 m" Y% n9 n最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
* u; g$ _$ x7 d- i2 j3 P+ I1 C5 U& `/ N% X5 c6 C; y
1.2:使用Timer
' G9 s; F' j) s
& B0 e) ?: K; i查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:" B9 ^! c( r0 {4 p
" f: c6 n A3 A8 w% Ipublic class TimmerTest {4 U$ ~4 P' Y4 w& N' `! P: p8 c
/**0 y. b- B. F2 {/ o F
* 测试方法
; A& U5 y/ a9 m$ G6 [ */
0 `! X6 ^9 o2 h( N! p public void test() {7 e: V2 q7 u7 `& C- R( X
Timer timer = new Timer();+ c( |' O" S4 b: x# w% \
timer.schedule(new MyTask(), 800);
# D+ c* Q+ y8 `6 c }8 K* I# x! ~- [. M/ R o
' r( y: G0 R9 n, Q! P5 g public class MyTask extends TimerTask {: P1 P% Z6 u8 ^. O8 P8 P
* W4 \: F* ]+ v9 a: ^4 Y0 h6 B9 K) z
/**1 g0 [1 q' }0 l, y9 o) X3 S7 a( C
* 运行方法
5 w1 |. c. F6 }* \ */5 B' F+ i6 Z k) E( |9 _7 P/ u9 N
@Override
$ f3 Y2 L2 ~( m public void run() {
. A8 \; \3 C7 V1 x+ I O. j* h System.out.println("输出");
0 Z( s- ~; p! b8 h! W! U0 j/ ~) I }
+ \* W8 x0 ~4 V) h }
( u9 l& X: r* y; M( m c}
# E2 }0 c: }+ _1 `' n: h# ]& e7 I6 G这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。; o% D: _1 P0 A/ t4 W8 }! p- c- S
- u1 S+ i1 {+ T$ g3 d
1.3:redis延时
7 d4 l% d, i# ~# n
$ y1 s" k4 H/ x: s在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:2 ]. h: _" x. D4 Q! m1 ]( N0 w0 c' I
% q% Y% g! Y, n X7 L/ M) \7 ]. F( B
0 D0 S% x+ {( g- P5 ^6 D) D
& o6 y" m, ~8 y, r; F; @通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
: d' H8 S7 A' V# e2 q
1 r) z5 L) a1 y3 d, y( w3 d- b& V% t1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制0 p1 X0 H+ r, y8 E1 I& F" v
. N9 m" `! S0 V$ c: ?/ A! F; r
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现4 [4 P2 i. n8 ~4 c3 E
4 q# Z6 _) ^) O" s7 S8 c7 R" z3:简单,真正的代码实现起来只有很少,下面会给出代码示范。( Z' N9 q7 w5 X$ h/ d# M( f
! |: o8 @ i! T, `# f# H: ? V! R
二:redis# y9 Q8 R" W9 f0 B* \0 y
" B& p: t% n9 `! y. z6 w
2.1:maven中引入redis
' b) r4 v- u# ^: U# {* c/ U' z1 ~# ~
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
^! b* e% [& i% q3 c4 |% M' |( q2 W+ W& F. J) q4 f1 ]" q
<dependency>% Z: [( O1 h, v& {, `! B e1 ?- W
<groupId>org.springframework.boot</groupId>9 O! ^& s4 W3 r5 h8 t4 f- o) w$ \7 {
<artifactId>spring-boot-starter-data-redis</artifactId>2 b5 z3 }/ T9 o* Q# _
<exclusions>! b, X+ D' |8 m- A. ~
<exclusion>
# A8 T/ n- _6 R0 q- P <groupId>io.lettuce</groupId>
5 X% ]( o. s( ~ <artifactId>lettuce-core</artifactId>
) q' ]* ^% H, I, \9 s( O: { </exclusion>) {; S9 a$ e' G. |1 Q" H* Q
</exclusions>" w( ]# e: H/ P; ~, F
</dependency>$ Z9 z0 |2 Z& K% p2 k7 L
<dependency>2 w6 R1 H2 m( a* r, F4 q9 H
<groupId>redis.clients</groupId>( H0 W- r0 O; ]& `, E' y: h
<artifactId>jedis</artifactId>
- N8 Z; t- F/ _0 y) H</dependency>
1 x0 l! U1 [* _. H, W* _, c2 i* Z2.2: 在springboot中配置redis% n$ W& f! c% @* | o
- x3 Y1 G. `4 r. X& @& himport org.springframework.beans.factory.annotation.Autowired;0 |$ l& m" l8 ~5 }% D& Y: O
import org.springframework.context.annotation.Bean;
/ j1 a* o: l/ z3 K, Vimport org.springframework.context.annotation.Configuration;7 A9 c* C& C, y2 \3 l* A
import org.springframework.data.redis.core.RedisTemplate;. z* I$ o: D( P6 D! }
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
/ P5 j, G* {8 t* ]: Q1 ^import org.springframework.data.redis.serializer.StringRedisSerializer;
& y6 U/ s. V; r: r0 F# G, v
, H6 T; `. G% P; b' J5 J@Configuration$ O, U8 y) |' I1 y3 j
public class RedisConfig {
+ q# u- H7 f7 b( m7 _; j
. q/ {; ^: F/ ]8 Q" O @Autowired2 H- {. I4 g& p- }
private RedisTemplate redisTemplate;
% w. U, k7 `+ n. o" z6 X7 K0 u- [4 B1 j2 k8 m$ R/ _
/**
& G6 [7 u! R8 u) ]6 Z * redisTemplate实例化
( S5 e" y) z. R) Y. H. m- H *2 ]) E p: R! E7 p# {, l
* @return! b- N+ h6 D1 x4 s3 ?" i
*/# N p' o" J! P; V
@Bean9 R8 G7 H7 t7 U% ^+ V+ l2 _0 u
public RedisTemplate redisTemplateInit() {; P& P( D8 m+ C" t T
//设置序列化Key的实例化对象7 [) R. {0 z# k. R, c! c! B
redisTemplate.setKeySerializer(new StringRedisSerializer());
6 x1 F, `2 N0 |5 j# W& x6 ]) S //设置序列化Value的实例化对象
% m( K8 k+ w- e redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());: n" H* i5 J4 o, _
return redisTemplate;
: D$ e9 h) R$ \" g" @3 b" b4 F6 |5 D6 X } `8 v. Y/ y( ^+ }9 G, F! Y
3 v, V; p, O- K1 a}. ^+ `# f9 C8 C* a% }8 O
2.2:redisTemplate模板工具类
0 z4 e; D, d+ P3 R K; o
4 b2 |/ a9 K8 V@Component9 }, ~% _* l6 S1 b- x( @
public class RedisManager {
# w' [! O/ B: O- B# d4 f' Z3 Z q/ U! R6 N- t6 `
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);% s" s) B! M# W4 X6 _8 U$ s0 Q
h6 l9 O4 @' n
@Autowired. g( i7 o+ D# J$ A) g
private RedisTemplate redisTemplate;& X" X/ X5 u+ G0 G1 O( ^
. ~: _* A* k: f /**
! E$ ~: l' Z4 D2 Z3 | * 设置对象
) T5 H/ E1 E' q: }3 _ *: |0 C+ `6 |) ~/ E; @, W
* @param key key- V8 U5 G$ ~3 U7 _: h j
* @param value value值
$ W8 Q1 D# H& Y, I- K * @param <T> 返回值泛型% `2 ?0 d! A, x7 w& f
* @return 正确的值:<T> 错误的值:null8 v9 p+ g: j2 f
*/
- O e) i4 D) C. I4 R+ a2 [5 g! U" p( I @SuppressWarnings("unchecked"). u" p. q' z! @: J% f; n
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
* y1 B" O1 T+ T+ B4 S$ S final ValueOperations<String, T> operation = redisTemplate.opsForValue();, b P% w/ F* h7 {7 \
operation.set(key, value);
+ H$ p/ G/ z L1 k, F! b& Q2 y return operation;+ B* c( w" d2 B9 {/ Q. ~
}
' b: Z% o+ {& j
) J% k; ^! U8 ~# E6 \" d% }: z( Z /**
/ R9 R' h1 I; K: n h1 i2 I * 设置对象及失效时间 (单位:秒)' D8 t; r. r' t- N; a$ [# D6 T3 N
*
' |) b8 x% G8 q% J * @param key key
) R4 o+ j4 D# y( `/ z1 S) { * @param value value值5 [7 q2 u9 Y8 j' j2 V# c8 U
* @param <T> 返回值泛型
5 ~& h- X1 @- G: q: x+ S+ F * @param time 秒值
' S2 g* W% f5 t8 y6 T( P7 U4 e" m3 b * @return 正确的值:<T> 错误的值:null2 [. x/ P& ?6 e7 l: H; a4 S2 S
*/- V4 E4 n9 h y- B( ]% _
@SuppressWarnings("unchecked")# C1 {7 {' z# P# @$ P! D% I
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
2 {* A8 d8 |" D. f1 s1 q final ValueOperations<String, T> operation = redisTemplate.opsForValue();3 H' ]% L M" S/ ^& M3 I; b
operation.set(key, value, time, TimeUnit.SECONDS);) w U' b! ]& Y( G( w8 y0 ^2 |4 C
return operation;
$ E: H7 z. [* z% p2 t/ S }
7 z0 M% O0 m, c, q }* `( |
% q0 ~" h' N' U% X' D# b( |) `: G2 v$ X9 t3 S
/**
) o* U2 v- N$ Q1 P- f+ v * 设置对象及失效时间(单位:毫秒)
, b6 k% W; d3 Z5 W- I *
% k8 @* v4 y2 u* z# [8 t * @param key key+ g, D( R7 g2 \# e5 u
* @param value value值; T4 `* z) W5 X0 M3 a9 s+ q
* @param <T> 返回值泛型" `' A$ ]3 a% Z/ K
* @param time 秒值' h1 o' c# X' q, P" E h
* @return 正确的值:<T> 错误的值:null
+ |: A+ S# |" n1 j+ @ w6 M1 P */
) S% n7 g# M. P @SuppressWarnings("unchecked")# z8 i; ^* X0 X" m/ S
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {9 H# y3 o# q1 q7 X: L$ D. e) {
final ValueOperations<String, T> operation = redisTemplate.opsForValue();" @4 w$ a3 Y- q& t
operation.set(key, value, time, TimeUnit.MILLISECONDS);
1 t J( ^$ k" J2 f( P* {( V return operation;
( a6 Z: G! Q- ^3 J7 H }
; {9 m5 |) l8 o6 Y9 [+ ~# q o1 o
: U F) o! B3 j. s /**
* w* q8 T, q4 I7 E6 J * 获取对象& N! ~' s# r9 E) L3 K
* G y+ C$ a) R \+ d$ u' Q0 x
* @param key 键- s% ~' M( l" J( s* E$ I
* @return 正确的值:Object值对象<br>
z7 [2 i& y8 U1 i* Z' I * 错误的值:null, f( E4 I4 V5 I( w) H1 H/ Q; c
*/5 U3 Z5 x& g5 @9 ], Z9 O! l
@SuppressWarnings("unchecked")
: }: S. n. q$ f, S, N public Object getObject(final String key) {3 y+ ^ H' l; M& V) t
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
( y0 |4 ~) I/ X0 O' X6 V6 H if (valueOperations == null || !redisTemplate.hasKey(key)) {( n9 L- Q6 v2 i& m( z3 v5 H
return null;
% o6 H. r; n) v9 ^3 g/ @ }- _/ B5 }; h) P5 r1 [ {
final Object object = valueOperations.get(key);
" E' r t; }9 W$ I) f6 M* u2 e* K1 K return object;1 S$ r( J* k! i3 F2 q4 Y
}- A( `) c1 w |, t& b2 `& r
1 U5 d7 c$ ]. u6 o0 w /**4 R3 h+ c- H7 y% h5 n% J
* 从缓存中获取string值8 d+ _% x0 [! l* y) X! G
*2 j1 t5 q* M5 V5 B0 u
* @param key5 o; P% `8 [- j9 e
* @return*/" y8 b0 w- Y( ~' b
@SuppressWarnings("unchecked")" r# u% [1 u' f! a; e6 Z2 F
public String getString(final String key) {5 H' K2 \8 ? V! w s( D
String value = "";4 f8 b# f+ a. o# M: u9 R3 R3 U) @, n
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
* Z* w9 c6 U9 j$ O) W* ?: j if (valueOperations != null && redisTemplate.hasKey(key)) {& I% w% d2 b' V. h
final Object object = valueOperations.get(key);
2 \* i" U3 B4 V3 z* ?5 p' F if (null != object) {- Z6 B: a H1 T
LOGGER.info("--getString--object not empty");) X+ C/ j$ H1 {' M: i$ \
value = object.toString();
" K. n; T2 A# r+ v3 m6 Q } else {
5 S4 [+ F; C. i7 M8 d; _" k% G LOGGER.info("--getString--object empty");
+ a1 b, E' s& R' D: r+ s }5 \- ~9 X3 ?# O# e6 n
}4 n1 N q- I1 L! O* x, T
return value;9 e# b" z1 H9 w2 S
}! k, f- y% P3 _% D# A9 m' q
2.2:在redis中实现时间控制
K: U# |1 g% q* s. P* ~1 O0 A4 u& V0 t- S) i: L
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
. P% `8 A# R) X7 Z( e$ \7 c. V4 L
% U& W' S' N L4 m' X/ j8 Oimport com.youjia.orders.redis.RedisManager;8 ]# O' E) K/ V
import org.junit.Test;
8 s$ p; w3 e' ^3 `" G9 t0 J7 himport org.springframework.beans.factory.annotation.Autowired;
7 l3 `! b8 \2 n( ?! w! [ f! @
d: I h% T# }( R; r' Himport java.util.Objects;3 G+ Q/ _1 z3 H
8 S- H6 \) `3 [, E/ e
/**
* J( O4 \! c1 V2 X * @Auther: Yrion
4 n) ^3 i: y/ v { * @Date: 2019-01-11 23:36
% ]+ ^/ _0 k8 f# @/ G */
6 S; u8 D/ P* G+ E' x3 f$ I6 _3 E8 _1 q2 m# F
public class RedisTest extends OrderProviderApplicationTests {6 ^1 A1 \; O2 d: Z0 V
; t, P! [: v& C1 ^ E @Autowired
5 y7 X0 z. O/ j7 l# X4 }9 l! n2 @ private RedisManager redisManager;
) J/ `5 H$ q* W3 P: l1 {2 E$ K) _
@Test
) \# b# z# H: a( {, o public void test() {
' Q% P6 } [$ o controlTime("10000001", 10L);
" j& P8 ^# o$ v" S7 z& o, G; g }
1 S7 J) b( F* s3 d' J# E, `/ e& p& d n
public void controlTime(String requestId, Long timeOut) {* l V! S4 e& Q2 G/ T* ]
( d, @% x) O& ~7 G' D& V0 D0 Q% s* ^
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {7 D8 K6 r" o8 {" x7 B% _# L
return;: o7 w0 u% t2 d1 } ^
}% V2 v3 e3 i! D9 ?3 K
//something code
' C. Z$ D$ u# K final String value = "value";9 `) I+ A) _+ _/ H! F
redisManager.setObject(requestId, value, timeOut);
6 n& y) B. F0 }% V final long startTime = System.currentTimeMillis();
+ r+ `: ]3 I4 N0 z) n3 z' E System.out.println("开始控制时间");
2 ~5 U I% ^( X6 N+ I c1 b //start( Q/ v% [% ?7 ]. a6 x) X- }: K0 F
for (; ; ) {% j' G! d! j- s6 [
if (Objects.isNull(redisManager.getObject(requestId))) {
+ ^, u& {# s- ]. l% w break;
Z: F$ c2 l ?0 J; s, T8 D. Y! z }
0 y4 A+ z5 L( c1 v ~6 @ }! o5 ]# C, s; ]% n
final long endTime = System.currentTimeMillis();1 d; `" J) A7 X0 v: c( U: J4 W" |: s5 U
0 f6 o7 I6 q- K; {* c# u final long useTime = endTime - startTime;5 z4 \- k* w1 [ l/ T
! \% o- u, S+ C9 d' C System.out.println("一共耗费时间:" + useTime);4 ?1 }, Q1 f, B( n9 Q' I
}
- \' c$ f& E: P, n6 A4 H}0 W: r3 [0 y5 L% ^6 K
outPut: A/ R9 H% L8 d: o8 d4 j0 k6 y. }* @
$ R+ `* e4 R# T3 ~1 d! `
开始控制时间
# ^2 N S, \5 c5 [, x/ `& w一共耗费时间:10042( H. {& |% r# A, E1 @
三:总结" ]' v2 w$ c/ h7 _- d1 t
6 Z3 i# w5 t& W8 \0 B% q本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
3 u: \# m7 ?# d/ \————————————————
+ h$ y* ]) w) m% j0 R版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
9 K- m8 N- f* N. \, g$ I9 I原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
' ]3 t; [' J( ]0 W' A0 H9 W
. v/ G2 ?( j, `2 Z5 `. n, e3 L7 H( ]2 }7 H5 X3 C& ~6 u% `
|
zan
|