- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 564647 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174617
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
. p9 u/ i* d, u& y! G- d J( gJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。 k, D: @. c( [* P' M
" C9 h/ D/ B6 d% j0 T5 T一:时间控制的几种方案
) Z! H5 C0 q3 N2 b- q2 |; @
( ^& P* y) R/ @( k1.1: 从线程方面解决
1 l& q2 R9 @4 z# d7 i; G2 g6 l* t- O) `" S2 Z# y# q6 i) @+ P& A
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。% y- Y. j, f0 ?0 a8 ?- Q+ q
' a4 I7 V/ X( |" @1 a
1.2:使用Timer
' x3 g( C1 X" `; h7 H7 _/ I
. R4 s% j% ?* c/ t" |查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
( m2 g# h P; U) a" O! u- P7 O" n5 ?. T' `9 }% N y
public class TimmerTest {9 p- J- O, B( c2 V. x* x, S/ F
/**8 }" n+ V$ ?4 z' A; J
* 测试方法2 l/ R. F4 z! P; g
*/6 D) `* H* q+ |0 b! ~# p( X X2 `* f
public void test() {% x) N+ S Y- ]$ e3 Z% {' N# }
Timer timer = new Timer();' t2 {4 v6 ~& v7 a; k) A7 b, F
timer.schedule(new MyTask(), 800);9 n. q S: D/ m6 ~# M+ a
}
; z Y+ e' d3 ~) H: G x$ n3 S/ j) a; e1 E/ l
public class MyTask extends TimerTask {
( d1 r! ?" O% q) s" Y- e+ j
+ U! m& {3 l* U+ U /**$ G/ j6 I2 f: s& L$ W. J& u1 P/ C
* 运行方法
" j z) g; c# K1 T& b- ?/ F( Z */
8 e# q" C1 Q2 q6 k( q$ o8 O @Override
+ |/ e) O" p# @. X; z) _7 F public void run() {- y s( K! v% w* |3 J
System.out.println("输出");! D3 N; }8 w/ Z' n" `/ }
}
; k; \/ r& P% _5 a/ m: Q( S$ U }
. M3 z7 H9 X6 i& A1 z# }}
5 x( _4 e# _4 q& J- \/ b6 o这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
' x- P8 { K, X4 O& M, @
5 y- n5 u7 j" n# D3 B1.3:redis延时
$ L- n X, `9 T2 A) d1 v5 f
: S2 d7 ]8 e' G+ D在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
7 z6 t! @1 Z0 Z, S' V- {5 o4 s. v8 ^7 j8 ~
$ ?+ o: S9 G# D" m
- N$ o$ l. Z" @7 K4 ]0 A通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
$ g0 A6 O( E/ N4 {+ G' W6 q
M) [% J9 ^' E/ n1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
! a+ D7 o1 D9 O9 ~, z6 x) Y% j4 F) e
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现* S" p, V, I/ z0 G
" a" S/ _$ P+ w3 `- S3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
$ ?8 N+ \. t5 b% C8 f, `% t
6 k5 ?( ?4 |7 z二:redis0 a! ~0 ~9 O, n3 O
0 }) v( N& S4 D/ r
2.1:maven中引入redis
5 ~" B, U4 K# ?$ |; D( h# k) z
3 s, L3 @' c, p3 r引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
& Q1 i6 }2 ] H4 x" E2 ?$ C+ }3 m2 F/ j& M3 j$ ~6 l
<dependency>
& M1 e$ K: K9 ~+ B <groupId>org.springframework.boot</groupId>% L# }( {; I, z; }4 {- g! v
<artifactId>spring-boot-starter-data-redis</artifactId>
+ G' P5 z K2 y9 P <exclusions>% N+ ~# c" D% k: P1 O
<exclusion>6 `# ~4 _; C4 q' X6 n& B; @% h
<groupId>io.lettuce</groupId>
3 z6 G& i$ H* ]2 R+ I <artifactId>lettuce-core</artifactId>
$ p/ d0 L" T- N8 c* \9 x3 j8 Y: ^ </exclusion>
* _9 [5 ]! k3 c1 E$ j5 B s/ @ </exclusions>
$ O d3 \' m) E& ]4 P</dependency>2 M3 o, `8 \- k# j8 |/ f
<dependency>
* U* E/ l9 i N3 u: m8 i <groupId>redis.clients</groupId>
7 Y7 j3 A: n; C. s/ U. H7 T <artifactId>jedis</artifactId>
# g& }- U- E) k) U6 g</dependency>
; r+ _) k/ O" b1 I, s) @& q0 B6 R2.2: 在springboot中配置redis9 v, S6 W6 W. s/ t
# {- n& Q2 {/ K. \# Aimport org.springframework.beans.factory.annotation.Autowired;
2 q1 y/ A# `/ h8 ximport org.springframework.context.annotation.Bean;
+ Q/ P# Y9 t' f ximport org.springframework.context.annotation.Configuration;- S) P8 }( v9 O% M% k% ^# e. A! P
import org.springframework.data.redis.core.RedisTemplate;9 z/ b* [5 x+ v6 Q- o' X
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
5 ` Z9 R9 I% x: k3 @import org.springframework.data.redis.serializer.StringRedisSerializer;
, V, x# s# w% j3 c( A1 E T4 `; F9 h8 M4 }* Z( V: ~+ g, c
@Configuration
) _6 ?" f. q3 Z4 e L, Lpublic class RedisConfig {5 a/ |0 G" j% |4 k" }9 L
9 |! }4 \5 ~+ {: t4 C7 A @Autowired
1 z% S! l9 R+ _) { private RedisTemplate redisTemplate;+ f7 R' Y6 V5 P* }
0 S9 Y8 B3 @; o /**# |- O& v5 U: b' {8 v, H
* redisTemplate实例化' P, m- V# q! k. W0 I- V5 P; R
*. {" J8 o4 r) g
* @return
6 C3 B9 j. W) W( l" p/ C */
$ _: } F$ @! Y# m$ ]% L, w @Bean/ j9 [ |0 R' R
public RedisTemplate redisTemplateInit() {$ k7 R: H- {2 u% g# E5 z
//设置序列化Key的实例化对象& }. R$ t4 E" K# t. y1 e
redisTemplate.setKeySerializer(new StringRedisSerializer());
; ~) m- E! L0 t4 F) P1 ~8 R) m //设置序列化Value的实例化对象
5 p1 `0 Y+ Y* F4 Y" Q redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());6 ^. y8 Y ?! I* R% P
return redisTemplate;
7 j' Q7 ]& N* q, d: u- { }, Z9 z; G& _( m" ~7 o# j
% i2 i6 g: \5 L7 b1 y}
& s5 U# R6 v0 x8 B. {2 }) X2.2:redisTemplate模板工具类
% B1 x! N5 ~. x/ m# h2 y9 ]! w- N/ h* I: G3 |7 m0 O9 }/ E4 u0 j
@Component* f" _* \- y* G2 v
public class RedisManager {+ E4 }/ {& t* P- K
9 c# Q. w9 r) l3 D6 S/ V" `) G! X
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);# S8 ?8 ~) B6 _6 r+ Y9 U* g
2 h/ O9 h7 r* s; J. {. B% w0 h3 }
@Autowired# t1 C: W5 B9 c" I& ^" Q1 g! f
private RedisTemplate redisTemplate;
. [0 F! T9 P% Y7 ^- i0 A( x/ z+ t2 L7 G
/**" ~% Z! j2 M6 K. n' o: ?9 X
* 设置对象
# H5 t$ e- E4 ]9 z *) T: i* g- m/ J; S
* @param key key
' |9 y+ I+ S; k: ~* ?* P4 P * @param value value值8 S, p4 |5 k' X
* @param <T> 返回值泛型
. d" D2 n4 N/ [% e g3 ~ * @return 正确的值:<T> 错误的值:null3 O2 l. j6 e8 `* D$ _
*/. R' [5 |% L7 F2 r1 E, v9 m
@SuppressWarnings("unchecked")7 U0 j) K& {) h" \
public <T> ValueOperations<String, T> setObject(final String key, final T value) {0 c8 O _, Q- j1 j" J
final ValueOperations<String, T> operation = redisTemplate.opsForValue();3 O6 j! _7 @6 f3 L- E
operation.set(key, value);$ h' l: a4 [4 P: O$ T- J' r
return operation;
$ g2 B: d; Z9 x }, p6 f% P8 X0 Q
0 Z. _) y3 z. V* }( ~
/**
" r3 m( @1 L" t5 i6 z * 设置对象及失效时间 (单位:秒)
( e3 M; r& S# n/ a( F) A *+ D5 C/ L. o9 K5 o" B
* @param key key K' K, O1 J+ z' d6 X* ~* Z
* @param value value值
9 q; `# }# a, J * @param <T> 返回值泛型
G6 \3 `% ]+ S! c( S/ g" Z * @param time 秒值
x! q) n# ]" R6 ?) [ * @return 正确的值:<T> 错误的值:null
1 O7 S% U7 M" p3 `; {, S# z */
8 ^9 ^6 e6 Q6 m4 g2 R- g) z7 b, l$ G @SuppressWarnings("unchecked")
% Z% M8 H5 u% F' t public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {6 O+ N. J9 v* ]$ m3 G: F# ?) G
final ValueOperations<String, T> operation = redisTemplate.opsForValue(); e! A8 c$ b! E6 A* ~+ s
operation.set(key, value, time, TimeUnit.SECONDS);" T) {4 v* g9 v! A+ {
return operation; ]2 ^* b8 |' Z+ v- k
}
. ~1 G. Q; r5 w3 s8 q6 s d# ?1 J& ?& V9 }
" G* a9 H+ o7 e, z1 n+ U
/**
. s! O, m- `: |' W# X1 F7 A * 设置对象及失效时间(单位:毫秒)
4 l# e: B7 t8 g. Y4 i *
$ m- u6 `8 a) a" p6 P * @param key key
" K2 C" g. `: K, x* i1 n * @param value value值! L7 O4 M6 s/ Z. H
* @param <T> 返回值泛型8 G+ `+ m) P, m1 K% i
* @param time 秒值
1 w% b& g% x8 C/ o' @9 }6 W * @return 正确的值:<T> 错误的值:null
& F: v# \3 w1 B: C7 u */* C- `$ U4 G! X5 A0 {* [! j; s7 h
@SuppressWarnings("unchecked")
& [( C- P9 G0 K+ I/ { public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
4 X: c5 b. N. V# R) P% Z final ValueOperations<String, T> operation = redisTemplate.opsForValue();) T: W% M3 g4 U% c0 _2 `
operation.set(key, value, time, TimeUnit.MILLISECONDS);
! T, q$ X! z' V x1 e return operation;
. V% F% V6 x. \; x }, d! t7 Y4 ^" \
' w0 a+ x# i0 @# t0 G
/**
* ^9 P9 M8 E" Q6 z6 J * 获取对象
2 ~" K. ` @/ E- e *
% l6 c" _! a" }1 v * @param key 键
6 z# j+ S- I1 s7 w3 \0 d5 `' G * @return 正确的值:Object值对象<br>9 a. |0 ^) t' v7 X- p. X, S( K, z' @
* 错误的值:null \+ ]( p' s9 T! R
*/
5 Z/ K% m5 S' h0 f: p: X" } @SuppressWarnings("unchecked")
6 h4 N: m1 t' ?- U9 w p; d6 q5 i public Object getObject(final String key) {
$ L: d$ u' O3 w' F* A0 N* V3 L final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
+ j, L7 d2 p4 k$ D0 Y5 U, J1 s if (valueOperations == null || !redisTemplate.hasKey(key)) {' H/ n2 ] i- E1 P, `" r, ~
return null;
8 N9 C* F5 v- c& K1 m }
/ T4 G( Y" Y3 @3 \* D$ Q final Object object = valueOperations.get(key);4 _7 ^4 o& c6 y
return object;' G3 J7 y6 B2 T7 Z/ X1 H
}" [8 \8 O: D! R2 u1 c* f# m1 X3 a
^" M% |, u0 d5 A+ Z+ ?) }; Z, n /**
7 G" N8 x! ?4 L& r: n * 从缓存中获取string值, d& K7 ]& R7 Q* l( G9 \+ Q
*
1 z& \5 h7 h! p+ `3 p * @param key- o2 r0 V7 m, R: O
* @return*/
k- M+ U# R) Q. m6 ` @SuppressWarnings("unchecked")
2 r$ X1 F, Q$ G9 R+ M public String getString(final String key) {+ |% S% P! n/ w- j( x4 |% r
String value = "";; P: r6 [8 B% Q5 e4 q
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();" n }$ w$ B: h* f
if (valueOperations != null && redisTemplate.hasKey(key)) {
( c- a2 @0 c! h0 K final Object object = valueOperations.get(key);
3 M, N/ \& g" Z! k) R/ S if (null != object) {
1 l8 k5 ]. k0 k: M8 h1 C& S LOGGER.info("--getString--object not empty");
/ B" K3 u" z# t% O* U. @' b value = object.toString();. n; H* Y- {: r: w) g. ^* A8 s
} else {" a) ^- `1 w# l% s
LOGGER.info("--getString--object empty");
% f% q7 W3 s4 B8 V4 v5 w }0 [' J( c1 P+ b, \; D
}
; y( i) a1 h4 z return value;& U3 q" B5 Z+ s7 J! Q8 M5 \
}6 T" c+ [, H$ X7 u' [2 P* `% w5 j
2.2:在redis中实现时间控制7 V( c% z, @4 c6 k' R
: S" h) K( d0 A' ~8 K2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。, h/ [& j; {! A8 D
: d, x' T! c9 a- z; A0 wimport com.youjia.orders.redis.RedisManager;9 g4 I# o- ]; a. r0 f3 m
import org.junit.Test;4 ?; Y& W1 {3 ?% l+ ?3 @: C
import org.springframework.beans.factory.annotation.Autowired;
5 @/ N$ U g: ^0 r; Q( u, M
0 r: d& [' \. ]2 L- ?9 K- q3 Z' wimport java.util.Objects;- s) `$ L* l5 P+ l' ^9 t
* J0 R* o M: i! [7 A* [/**
0 O& Q( X0 s @# d' r * @Auther: Yrion
& h5 Q/ ?; ~9 v, h) V1 N * @Date: 2019-01-11 23:36
$ J) a" B2 _. S! \) {# r */' {" T4 f; u% [) {0 o5 ]3 n: j6 W
8 F6 b: q. j7 E5 h7 Cpublic class RedisTest extends OrderProviderApplicationTests {
5 T# X& @, s4 ]" r$ a, Y; B
- h% k! c. M' d; F7 r @Autowired3 k3 q9 }; C$ V7 {5 t+ O0 @! Z
private RedisManager redisManager;
; C' F a# N1 k6 P3 }% h; c
" y6 a9 s" u9 L- E% V" J' u* g @Test
3 }) w# i5 y: S2 \( ] public void test() {
; Z! W6 ?4 X: x$ j" M! O z controlTime("10000001", 10L);
. b0 H4 w" M' o: ~. ?* r }8 m- _7 `( K+ }7 a- x+ t
; S' W5 c; N0 C3 Z9 H# e
public void controlTime(String requestId, Long timeOut) {' a6 p0 } b# y4 H$ Z. n
( e l& ^3 p2 |+ @+ r
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
6 q* D# G" o0 r% z9 K4 [7 z return;. l" j3 v R5 |/ K
}( e4 E% N' v! t( L; p" l" m& C
//something code
. h1 K& Q+ ?. K4 S* i7 O final String value = "value";
z( W% A7 P6 q- } redisManager.setObject(requestId, value, timeOut);2 i! S, P6 \7 L. C; g4 S0 u, O
final long startTime = System.currentTimeMillis();
+ y a+ R; w6 q5 C4 } System.out.println("开始控制时间");) [; _3 e2 u3 m& L
//start' ?+ ?8 v+ F1 n! C+ j
for (; ; ) {& c: w/ F# `( r' T
if (Objects.isNull(redisManager.getObject(requestId))) {: {% K2 {8 b, p* ?/ }
break;
( Z8 J: t4 Q4 ]- T, X% j$ K }
3 _1 J% v3 T4 M& { O3 ? }
7 G1 t, W0 k. Q final long endTime = System.currentTimeMillis();. g& q) l9 ^8 D7 A' I, Z A
5 I! ^) [7 }5 Z4 X8 e! N; N
final long useTime = endTime - startTime;
! Y3 _2 p1 l7 [/ ?. F, s: r+ G6 ?! O0 p
System.out.println("一共耗费时间:" + useTime);
% U, H$ E* J9 n5 F }5 B" X# O; j6 [+ o+ U, R7 I
}
2 a( I7 s6 k. F. N( }outPut:
' u- R1 h, g- H5 b
7 m6 Y0 m& j/ t! j# S开始控制时间
$ y7 \" T4 I: O N* n1 U( S一共耗费时间:10042
: k: l/ T% z! b% H9 Z三:总结
& J$ ]/ C- U5 n: H1 I% P# h" A4 x& a8 F+ p E3 s
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!" z4 g1 d- w3 a
————————————————
- l% L1 S) T& a3 \3 s版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
1 R- K3 n$ [4 o+ X, |+ E* `; ~) x原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
H' H$ S, `0 x t! z8 H( e/ ~5 e/ b- i* O7 P6 I8 V" t
' U/ o- {6 C7 O8 n- \4 \8 Q# k; P
|
zan
|