- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 564648 点
- 威望
- 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年大象老师国赛优 |
! n& s3 @1 w& S5 u0 U6 Q) EJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
B8 ^ y& a; m9 \# x& y
" f' i3 h0 R: W5 k$ v一:时间控制的几种方案; l4 W8 m0 {; |6 v/ u; P l; O
' \) [' `' t# m( l
1.1: 从线程方面解决
4 U+ G2 }/ f( j$ y0 s% Q0 l- Q/ {) D# L- ~% m u. B" G1 o( A' e( Y! g
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
, [/ ~3 ]5 W0 I/ q' d& |9 Z0 R2 ]0 p/ W: h/ v1 R i
1.2:使用Timer2 d& @9 ?! W$ x M; O- ~
# Q' W! l& v! x查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:1 `3 |1 {1 g4 V4 C. |
! X: A/ Q8 h" `% D) y
public class TimmerTest {# P" t" w+ I. P" C( u; k
/**
9 w' @" L! X( T$ H8 q B- } * 测试方法
' T3 k5 H" \8 h2 N */7 r! ^% g7 A* ?2 o: h0 n
public void test() {
. J& q* O8 Z: s7 R! J Timer timer = new Timer();
0 H* C- |, J8 R+ e timer.schedule(new MyTask(), 800);+ d5 F* f) w" }
}. {& s) w$ ~, W" G
9 W/ \2 [, ?, c- B$ s
public class MyTask extends TimerTask {
) N7 j" O z+ x# W. T' | A0 t6 j7 G' l2 s; m# J
/**
. p" J2 [" R/ z. {* D% d2 I * 运行方法
- x! s8 i, g% B+ R$ p */
% p! i0 B- e- u @Override
% D0 G3 Y" k3 k public void run() {2 m/ Q3 z. {8 j- |5 V' c1 n
System.out.println("输出");7 |# `) a$ s1 J( t& A0 Q
}6 s2 h" l& B5 [$ |/ s' @; W7 B& w
}" Y( F/ j/ s8 W9 @" F. d
}
, d) v% _5 F; ?这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。) x& F3 R$ U9 h
$ W9 m4 w; B2 Z1.3:redis延时' C! g% K" e; s: f9 r
7 S- A5 b& J) D( z2 t: ]
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
5 |. I* a1 J" l& n
- S. W( L% z ?6 X" l* D9 \8 V# x+ Z, d! i& }
* j$ A; F) n2 K! k- ] R通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:/ n( s$ g+ m4 p: {
7 v5 j- V* _) q" V# ?3 r1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
/ Z' h5 H: b6 ?4 O
9 w& u8 X9 ~8 ^. b P ^7 v: I) I2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现1 m1 B" F% ^1 g0 \2 I0 w/ l7 G
) \8 _' c7 D' o$ i5 K. q0 ^3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
9 u' T o; X" U9 ~+ a8 D( J, v d* t' Q7 p5 s
二:redis
/ I! Z' L0 O; x- [! d
# x `: t" o/ f5 u/ P7 H2.1:maven中引入redis2 h3 g% r1 y. D- x; P1 v
- H8 U) C! i, \ d# z4 D1 F: @8 U- T
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
6 P6 m* p" p9 W
6 j$ b: w: D: ~8 x! k1 ~; Y<dependency>
* U1 _1 {5 O! h( n$ a" j) d5 m <groupId>org.springframework.boot</groupId>; W2 Y5 I& f+ G3 X& L& @+ `2 J
<artifactId>spring-boot-starter-data-redis</artifactId>
9 a* D; C2 K; p/ \9 A <exclusions>
& R; N: H4 X' [ <exclusion>
1 n- u# @1 s" t) q* V2 U <groupId>io.lettuce</groupId>7 v( q. e1 P# Z: [! o
<artifactId>lettuce-core</artifactId>+ Y, U% M3 a7 G2 d# l! @
</exclusion>; X5 V# o1 i7 k( l( m
</exclusions>. X. Q/ A/ S! M _
</dependency>1 {: i1 O7 D5 U/ U
<dependency>* S: U9 E( y7 ^& m C/ m
<groupId>redis.clients</groupId>
4 z5 T9 N. D6 @9 E; r- ?) S1 x- I( C <artifactId>jedis</artifactId>/ P* M- N0 p* m, V9 b& z3 T" J8 X/ a
</dependency>
5 C5 t; h R# D5 s. k, l2.2: 在springboot中配置redis
! y) A5 z/ |6 `$ e( c3 n
& R+ L. F+ U) V v8 [% uimport org.springframework.beans.factory.annotation.Autowired;' t8 Y6 w1 H5 B9 n- d
import org.springframework.context.annotation.Bean;
7 j% [$ F M/ b2 |; a+ Q0 s" }. ^import org.springframework.context.annotation.Configuration;
1 D3 s+ w$ f2 ]6 y* Limport org.springframework.data.redis.core.RedisTemplate;
. \( x; {7 f9 @! D% Y5 \0 O; Himport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;: _. {$ V$ D5 ~# l8 n
import org.springframework.data.redis.serializer.StringRedisSerializer;$ O8 p' I: G4 M* O
! c, ~0 {, \- z# a3 f@Configuration- z4 S) Q' m! p; T( D( |
public class RedisConfig {
! p7 @, R" R6 | x. e$ g- a- I/ Y4 H' l/ K* a
@Autowired( u. O* Z" Q; p9 T9 N7 L
private RedisTemplate redisTemplate;
; z- b/ u8 r9 _7 p/ o% J
6 d* ?) i" Q$ `- E: d8 d /**
- Y5 U7 M' W9 g3 w' d' o * redisTemplate实例化' z; u4 C% d- Q
*
R8 Z% D8 r9 q1 t * @return8 q$ R1 H+ `- t: J8 I# i& N6 m
*/
3 _7 T; X/ p w# o! R+ N% F @Bean
& t! g# y7 |6 H, } public RedisTemplate redisTemplateInit() { }0 r# [, }. |" O4 u" H
//设置序列化Key的实例化对象% S* w- j9 M3 i: V
redisTemplate.setKeySerializer(new StringRedisSerializer());
: O7 [9 M( T8 @3 ~# ]- c3 G+ W //设置序列化Value的实例化对象9 A: U# ~' a) m
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
; Z$ U1 s' ~1 q/ Q, X7 D return redisTemplate;
1 l; f% W% t2 V9 t; ^ }
4 d$ w2 t. E/ m4 W. O7 E I! u+ e- D9 B! p; `5 e2 p# F
}
1 b2 d2 \$ {8 Y0 J/ P" J3 K2.2:redisTemplate模板工具类
1 r" U+ K2 }8 Q" e
- t+ D- |" l; t0 Q2 o@Component" l A/ F" j, x }" J2 e
public class RedisManager {
0 Y, z& O; a# H$ v" T& L
) E9 L. o& F3 N' | private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
, C I! a* G$ `" S/ I- C7 V. y# g
& I) }! \, m. u# ]3 S6 j; D, _ @Autowired
# q* ^" d+ H- M% T: X4 r9 q/ c private RedisTemplate redisTemplate;
; d5 |) A% |+ Y: d, m% S# w6 T8 Z" V+ `2 S' y& [
/**
5 A7 B6 @2 o/ @6 N) {+ b7 ^ * 设置对象; C* J ]2 p$ Z: L% q$ m
*
; g3 T m1 C; m& B$ M * @param key key
. h" V; W* ^) M3 p0 L' h * @param value value值
6 p$ H/ b: o" y1 Q/ l' B6 a * @param <T> 返回值泛型
6 L( ~$ M P+ i7 U( d * @return 正确的值:<T> 错误的值:null+ e! E4 @+ |; r! k. ~6 _" V
*/
2 R$ w) k. @5 @* u @SuppressWarnings("unchecked")
4 Q, E- J8 v9 N: {- _9 C public <T> ValueOperations<String, T> setObject(final String key, final T value) {
# c. O- c/ Y! R2 Q final ValueOperations<String, T> operation = redisTemplate.opsForValue();/ ?9 v% C) p' ~
operation.set(key, value);
* s* J" r: f- G: \ return operation;
6 \9 Q# L% o g+ Q+ X }0 p$ I5 W) T* X/ g' n$ J
$ p3 x# o- E/ q* ^ L( k& B) C: c
/**
, n* W6 {0 k0 t% }. P/ F * 设置对象及失效时间 (单位:秒)
2 [2 F- A) v! A+ P( N+ k *4 ]; q4 k6 I$ k' L* L2 z- V
* @param key key
# u2 g$ w2 W7 S1 K& `! _ * @param value value值* |) I% `# Q0 W# D
* @param <T> 返回值泛型
6 d5 v; p& l. o1 O# U6 X * @param time 秒值5 o5 P6 _0 k4 C+ |$ i$ {
* @return 正确的值:<T> 错误的值:null
, l3 l! G7 N( T( F' z5 f* A */- ?3 R1 S" Q% N- O7 ^- h
@SuppressWarnings("unchecked")
( n$ ]2 I1 q" _ J public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) { G: N' x; l; p( n" ?' u# W
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
j m. s, T; [. Q$ F+ ^6 |0 K operation.set(key, value, time, TimeUnit.SECONDS);
0 s- c4 R& ^3 ^& \5 S: Q5 E1 Q% F0 x return operation;
2 g4 f7 E! v$ Y& l0 t }
# x2 q% M0 f# g4 v! N- d S1 _( j' S
5 i6 K( p( c3 z% R. W# P& z7 X
/**" m% ]5 @& \0 g. Q* O
* 设置对象及失效时间(单位:毫秒)
1 a2 d" Q6 r) Y k& q *
$ ^6 u. k8 d6 M# X2 K * @param key key# i- k5 S# J! O* l+ F! `% r' C+ X
* @param value value值, b3 Q9 E$ X' ~* u) e, b
* @param <T> 返回值泛型# l z- z: `/ M
* @param time 秒值
4 W0 H- A. l g; P" g0 V( v# V/ [. d * @return 正确的值:<T> 错误的值:null- L; ~5 C. m4 Y, A' C+ ^
*/% b& C/ z) [5 s8 D9 v
@SuppressWarnings("unchecked")
, F% T9 x, m1 k9 d8 X public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {( e1 h( R# F& [* w. @1 K
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
# Z A% |/ e% _4 O8 b% S operation.set(key, value, time, TimeUnit.MILLISECONDS);4 @! F6 P y9 S( y1 o. G
return operation;
0 E P3 i+ A: \0 j D5 h9 w }+ D6 Y5 q; ^' ]5 E/ K/ I# ^
# |# b. f k! B: L; V! ^" l
/**- L# H* E& h9 ]: a! b: H) m
* 获取对象
6 o) b- Q: l; H) W8 a *
1 R% ?$ T% C+ T y; a* x * @param key 键- x6 g" y9 D* T* J1 f! H
* @return 正确的值:Object值对象<br>0 K( g: j4 a0 s! m
* 错误的值:null
+ c9 j7 k+ y3 A) b/ a" ` */
8 u* y# V8 r: r# ]4 R @SuppressWarnings("unchecked")4 K0 }/ L, n& z& Q
public Object getObject(final String key) {
* h+ Z; c+ ^4 Z: P9 D$ W$ G9 @ final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
# {2 ?5 P- F! ^$ U if (valueOperations == null || !redisTemplate.hasKey(key)) {& N2 b6 ~! p8 B; h1 V( i) ]: y3 L+ S" [
return null;
; F1 Q5 `- _ ~$ ~ }
! j1 }0 Q0 M: |+ }; Y# q' r0 i* n final Object object = valueOperations.get(key);3 U' A1 z4 ]3 \, ?$ @
return object;
+ p v" u& {+ A+ o5 q }
% K/ {7 u- x% j- C% V" h# f7 f8 r- Y- A1 S) o3 U( d5 n1 ?* V
/**8 U) |/ o7 y& A$ I
* 从缓存中获取string值2 p; |" Q) @5 c, A4 J% U
*
6 _8 M# s9 j& U- e+ p * @param key& U8 D7 ^3 Z2 i4 L0 h, e
* @return*/
. c* A- B# T9 @9 T4 u( t6 W, E @SuppressWarnings("unchecked")
1 v+ }9 D1 c, ]% [% y( _ public String getString(final String key) {: H! m$ t, z1 P5 {) Y* W
String value = "";, O. L% ]/ j4 B1 T) }8 N& X0 ~
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();# b; T" C; U% O7 K$ ^$ N
if (valueOperations != null && redisTemplate.hasKey(key)) {
4 o4 V- i$ I' i+ K& t. h! ^ final Object object = valueOperations.get(key);
9 M# s+ _8 ^& h: p0 x' O if (null != object) {: ~$ P6 G: x0 _9 j3 t. I S1 z# j W
LOGGER.info("--getString--object not empty");
+ N; x1 u/ ~; s" c5 [ value = object.toString();
, W$ y# q" @2 M } else {2 ^+ ]/ A! A+ u- S2 E$ h1 o
LOGGER.info("--getString--object empty");* R( E5 k N% M1 s, c5 B
} |/ ]5 c1 Z# u o V7 O5 a2 L6 y2 Q
}
$ ^8 k5 m! T/ G* Y2 L7 _; _: l return value;0 L3 E3 S4 `. N
}; j" ^* v3 p9 \. H
2.2:在redis中实现时间控制# ~& i2 B& v& p& ?! N% ^
, ?& O0 g) E1 |4 \* J3 K9 A9 Z' g2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
0 ^8 V% X- i, I$ l% L- n4 N' ] u# E- z7 `4 q
import com.youjia.orders.redis.RedisManager;
8 M- Z2 |) R: Z' h5 ^1 l timport org.junit.Test;
6 H: _. Y5 G/ w& ?- simport org.springframework.beans.factory.annotation.Autowired;4 k) x' y4 N z8 j! S5 p
' Q3 P2 i5 ~5 |6 Uimport java.util.Objects;+ m6 Q0 G5 {0 [% B+ z% d% {' M
" a2 R; j, K7 D- Z+ _( c/**
8 k7 l+ j) k+ [( X. H$ P( H * @Auther: Yrion" K( n7 g5 |4 X5 U: u2 v
* @Date: 2019-01-11 23:36" R& V% _* M! I
*/3 p9 I. M! j; Q6 x' W4 b$ T
; S! X* M- s% W0 T7 F( [
public class RedisTest extends OrderProviderApplicationTests {
3 \- \1 R" Y: ?0 A( M! ~( A. s: V
. J! n6 G" a; Y0 d @Autowired' B+ W8 ?$ N5 {+ {8 f7 H
private RedisManager redisManager;, M( Y# z3 t$ d& [! ^
# E+ r0 I- E4 q8 [. t# ` @Test
3 \8 _% U/ F# f3 m# u public void test() {
4 ]4 U4 E6 \$ Z# Q controlTime("10000001", 10L);
. z7 O! |- v, L( y+ D7 X% @$ Q+ a ^ }
( {/ m* s9 A! m2 |% S1 L- I+ D0 P% L& U' y3 r6 X& V
public void controlTime(String requestId, Long timeOut) {; ^7 E& u# k3 X; ~+ F5 u
! @4 C) F# o+ f6 P0 D' E
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {6 q7 s/ k' F2 c
return;
7 g+ n' W) i" v+ ^' P9 d' Z/ I! P B }
) r, ]. L% |8 M6 z% k7 K //something code4 c' H2 L% K/ S/ G; b* D
final String value = "value";
}& \9 E- W( K, S redisManager.setObject(requestId, value, timeOut);( @ y. i( Q* o7 U2 m
final long startTime = System.currentTimeMillis();
/ F8 W0 w* l4 V4 f9 M: k+ v System.out.println("开始控制时间");3 r8 C( N- ]/ G$ ~
//start! E, w5 Q; e. }) T1 q) O- n
for (; ; ) {) W( A: ?; P* t" e# a% e. c0 B3 ?# Q) ]
if (Objects.isNull(redisManager.getObject(requestId))) {
) {3 B3 X- o G' M8 R break;- h9 x5 \ A0 V6 e
}
5 V; b( {: r; Y/ V: N* G }
% V: r8 x" M% `9 U4 z) }+ { final long endTime = System.currentTimeMillis(); C. a6 g1 x, Q8 b% G# h2 X
! Q0 }4 f2 d# X" `
final long useTime = endTime - startTime;
) A4 r2 A$ v& y3 x0 D( |4 ]1 ^: m2 s! _1 g+ m! j
System.out.println("一共耗费时间:" + useTime);+ S1 g6 y+ W8 g1 v3 |
}$ ?7 K! J6 B. i$ g5 X0 o8 v% U
}
: U5 g, ~8 W4 l4 woutPut:' e' g8 s# h) E5 V% T( h
% m5 [! g8 X& o6 r( y
开始控制时间) I: e" D6 Z( e& `8 y1 A i& W/ G7 J
一共耗费时间:100420 k" L3 |6 g4 k9 h% W
三:总结; V" u5 }# o$ X' H
! I! Z* v4 h% c. g4 Y# z本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
* S6 |% }6 x& K% I4 }. ?————————————————
( A3 z. k2 c+ m版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
9 K5 g$ E O1 ?$ i- z& k原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325% V& n, o" |9 N( P
4 L! b# }4 k* V0 p2 Z6 E& f+ T. ]" @( ~
|
zan
|