- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 563414 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174247
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
# {8 I/ g. L8 M7 P A! PJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
* J/ D; K& Y- I8 J% o- n& Q
- C: c m+ J0 S一:时间控制的几种方案
2 R+ _/ w' p, ~# U& `5 ?
8 r. h7 { w0 v. _2 j& f D- k1.1: 从线程方面解决+ A+ J" w+ _8 A* W
5 P* x& G& |+ S& p5 _+ a. I
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
$ t% d* n5 X0 l, J) [- r5 V L
8 \2 A; U- O( f6 Z1 x1.2:使用Timer: p) I3 q4 b9 }- w
. d& }, B( O& D, ^查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:4 Y9 `* k, m& \, k S6 b
6 m% G. J: B% ~, j' I# X p1 hpublic class TimmerTest {2 J. `- P T5 ~1 [
/**3 A' g4 M( a/ C' |5 z+ I2 r1 T
* 测试方法
5 L0 x* A' [9 B" Y */2 L6 w9 T( T# r5 X+ @: B: ]
public void test() {/ h! [+ e1 x( U' D3 a. B1 o
Timer timer = new Timer();
( O% s% G2 s) H* I3 i7 w8 t timer.schedule(new MyTask(), 800); v6 }( V- W# [1 E7 b) @3 d; h
}
~! E8 s9 c* h: i& s; H( t7 i) @0 y- D, c/ f! j# b
public class MyTask extends TimerTask {
: \! ? b( h* y5 r/ e9 q- Y+ h5 K, E# W
/**' x. I7 f8 g' M* A
* 运行方法
1 w8 w5 U% u' D5 \$ E */1 D, m3 E, P- M
@Override5 C3 ]3 V# F/ u3 |. [
public void run() {4 g+ m' L; q- H. g
System.out.println("输出");0 W; y% d8 v; c E# E
}0 @5 N) l0 z; V- e6 w- p
}
6 b" J7 g' i0 [' }: \# p$ I}- t0 e7 [3 u2 ^) s
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
7 y4 f; {& z$ P% Z+ h
' ~* m: u9 P$ e- S+ `( S& W1.3:redis延时5 a0 ?& N% Z- e; [+ e
: q5 x3 h+ ^+ G2 }: g: }
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
' I) A! h8 M( [" q
! x* B' }% r k4 s' j( M0 g8 B$ O% A4 Y; s# Q
' I5 r7 U3 \; o, B2 G, Z
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:* ?7 [/ @" V" f! _' u h1 X7 ]4 g' |2 U
1 q+ r+ y& ?' q3 M) ?6 Y
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制% }* a4 N: ] V
1 O* k$ o5 Y8 K! L- P) O" s8 Z" Q
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
* B8 i) G; Q, ]4 ?! q% {. D+ A# }
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。; i8 p! U& ~0 v- Z- Q) F
3 D5 ^0 E! g! c; M' g. S$ f2 A
二:redis
9 Z4 ^+ X6 S, B* T
( Q; f0 d& a- c& Y2.1:maven中引入redis0 D! R- ^% S* b( Y6 B, ?
* C( \" s' J, T9 ^引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
0 y5 W/ b" O" ]% S) i U8 V1 |0 J2 E* W2 @3 o1 c; M
<dependency>
2 E, z! n; {- [+ }- E& n8 Q6 x <groupId>org.springframework.boot</groupId>
6 p2 n* o& U* X& p, `7 i <artifactId>spring-boot-starter-data-redis</artifactId>, u& i. q& u' G4 J3 s7 k1 o% n
<exclusions>
9 q: ~! q" C7 j7 I7 G& R <exclusion>
2 _6 z' w& o2 T <groupId>io.lettuce</groupId>0 w: L( b& A" v* m
<artifactId>lettuce-core</artifactId>' P7 l4 e) G9 {' `' ^; u, G
</exclusion> \. f% W/ d+ p. h
</exclusions>0 a) F1 ?3 C" ~
</dependency>
% H6 r6 Y& t& |. t<dependency>
% h, m, t: \- _; V% f <groupId>redis.clients</groupId>
% w' e8 h+ W0 U3 H) m. {' c) b <artifactId>jedis</artifactId>' I% [8 A F! L9 y# }
</dependency>
0 j) \- g; L( J7 x% v; K% ~2.2: 在springboot中配置redis1 S# K. d3 @7 \$ e! h" q) K; W
. X5 N( X H6 i G! O& kimport org.springframework.beans.factory.annotation.Autowired;" k4 d5 t8 k9 ?. n$ i% d( G* o
import org.springframework.context.annotation.Bean;- [* }3 U. c' ~+ I
import org.springframework.context.annotation.Configuration;
9 \0 n ]9 ?: }# A6 iimport org.springframework.data.redis.core.RedisTemplate;: m9 }" N {5 G+ D8 C0 n2 N; h0 ~
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
, z6 |; b2 e% Fimport org.springframework.data.redis.serializer.StringRedisSerializer;7 \7 r, O1 f( K+ S) S w0 z
1 E% w- V( [- r& f
@Configuration
6 X+ b( Z+ L1 ^ fpublic class RedisConfig {1 B# w# [$ g$ V
/ q, B0 t: C9 L) q$ u& ]+ |9 k
@Autowired5 E' I8 S" k6 O4 @
private RedisTemplate redisTemplate;
7 E( h9 [( i* a' t( V0 a+ N
4 W2 _% u* J/ A4 | b /**% `4 g! c& ?8 m
* redisTemplate实例化+ ~4 q; N$ s- u: ^4 _
*% ?# c9 i) x% |. c( R4 x' g: h
* @return; h+ n5 p' ?' l% e
*/0 U' W. D Y( X$ j1 ]- L- m
@Bean
, @+ w0 }2 @* J1 u. F public RedisTemplate redisTemplateInit() {
* x5 V/ _: P5 V9 d$ L //设置序列化Key的实例化对象
* B$ r7 B( I3 l3 V G% _ ^' V redisTemplate.setKeySerializer(new StringRedisSerializer());6 G2 m/ k" Y) j8 b6 } @* Z
//设置序列化Value的实例化对象
/ Q( n) j7 K. ~" c redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());/ g: Y- \: ~( d' S
return redisTemplate;9 G/ d w1 H5 I; i
}
% I! _4 {. N1 a: o" s. F7 N S
+ e; S9 o; x, x6 `, Y* D}! I- m& G( O$ Q0 I4 R7 T& i' p
2.2:redisTemplate模板工具类
: A8 ?/ r4 H% d2 e' [
7 i1 L4 R* f- j" Z* M: Q0 U% B. f/ P@Component
0 Q4 W3 M9 k. h" Tpublic class RedisManager {1 ]. u( B, W. G
( W" P9 D$ K6 L1 o
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
- L; @7 |3 y: m2 _3 S# h4 {' A! i8 g
@Autowired
# p0 g) `7 M; T% v" ?" K private RedisTemplate redisTemplate;
. R$ {* A0 q* m/ Q& ~( U& I! q ?+ |, X5 |& d
/**2 m) T! ?; J7 s) W% r5 ]9 w
* 设置对象2 g# j$ V, z' K
*4 s `1 @7 x& j( l5 t& o
* @param key key
' y) R4 o. u) O5 @' y5 T) e7 B6 n' A * @param value value值) Y1 d! p( c+ P. ]. L& a
* @param <T> 返回值泛型9 G6 d* w8 C2 j
* @return 正确的值:<T> 错误的值:null
+ S8 w. q! N( |- `- |5 J) o */
N+ k9 R0 c* S& A8 g& f @SuppressWarnings("unchecked")
4 K& A+ M9 Q x+ j3 Y y) p public <T> ValueOperations<String, T> setObject(final String key, final T value) {
6 m% }; H; T* K- E8 _ final ValueOperations<String, T> operation = redisTemplate.opsForValue();
( z( y7 D$ [& ?1 l2 U9 d6 G5 P operation.set(key, value);* r* |4 M# P8 K
return operation;: o R; u, q' |2 S' s2 w
}
8 e) f$ r! R7 D2 i& F1 K- z3 K7 {4 f& C1 e
/**- k" {' X9 O4 Z) C" c6 |2 y
* 设置对象及失效时间 (单位:秒)
4 `' z- f& j; A( W) s *
$ g" X8 j( j8 I& F * @param key key8 L" O$ k3 J9 ?: u9 @
* @param value value值6 G1 M! k4 x5 Z7 L
* @param <T> 返回值泛型
. ]1 K/ C0 Y% \ * @param time 秒值% N' Y9 B0 U% b" e! c
* @return 正确的值:<T> 错误的值:null E1 e9 q# D1 C( N5 [/ S
*/
- v* \& V+ O+ \1 L, w. C @SuppressWarnings("unchecked")
$ \5 }/ E& {% [5 q2 b q9 g public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
- }0 C( }# }+ d! B final ValueOperations<String, T> operation = redisTemplate.opsForValue();& u5 K. N( ]: L5 O5 Z: _
operation.set(key, value, time, TimeUnit.SECONDS);
6 t, y$ R; \3 e G) y, d! [0 Q return operation;
4 H& E9 |" {0 I, J& Z4 J( ] }( w8 q* I# ~- Q" F$ k7 \0 `" a
5 ?* P9 Q9 ^9 J' e' U
- P6 i4 Q/ a8 v$ {; y& K /**
8 Z- c; O8 z* a2 | @" P- ~ * 设置对象及失效时间(单位:毫秒)
2 u5 ^$ M: N' ~+ [$ g *6 D# `* q8 Z8 D% g/ @& I
* @param key key* R7 B- q. c# w4 Y6 p$ L; W
* @param value value值
1 W" q4 m' v3 n4 O4 U( d8 z * @param <T> 返回值泛型 V' u# Z; R' O1 F
* @param time 秒值2 c, `, \4 @# S9 s/ v# Z: W q
* @return 正确的值:<T> 错误的值:null
) h- e/ ?3 h! I4 t1 Y */1 g/ w: N1 `( a# Y+ x
@SuppressWarnings("unchecked")
" C& [& {* C& ?+ r8 ?& S6 R public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {% {+ O1 @( @1 T' w, o1 l' T
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
4 W* `% `5 r: B& h W operation.set(key, value, time, TimeUnit.MILLISECONDS);
5 {0 \! A, Z4 F" Z return operation;/ k/ d9 \; T7 D0 ^; s. n" P) K
}5 n' |0 J& h. I9 `
+ z; K ~, b* R# J% Y' O8 a
/**
, x ~% j6 a0 b& O. J0 y * 获取对象) k; Z! @0 u, K( `: f& W
*
9 V$ Z. P( X7 p& T( L5 u- m * @param key 键( }+ s( p7 O. L$ {) {) f: I
* @return 正确的值:Object值对象<br>- n6 w, T! O. d
* 错误的值:null
% f1 V1 Z! `0 f9 K$ i3 l */, n8 ^& U; |- l
@SuppressWarnings("unchecked")' D7 E% P% _- W1 d4 } F
public Object getObject(final String key) {
" v$ `) O$ i! _, e: [ final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
( Q% `/ L6 j/ ?* Y7 ~. b if (valueOperations == null || !redisTemplate.hasKey(key)) {7 W0 S1 h/ L: Y. a( i1 f% u
return null;9 U5 C- i1 h6 m' i6 w" ]
}
A% D) Q y) @0 W9 X final Object object = valueOperations.get(key);' z8 ^6 O2 E* }8 x1 J- k
return object;
- I: z& @% r0 h7 F }
; N x- H9 i' q- j/ \4 v( L* t1 q& n Z& _
/**0 p3 s8 J" S, Z: `& s% f1 P" t s
* 从缓存中获取string值
( s) d' ^, @6 R *
; g+ T) E* |2 I0 s3 S * @param key! o( b% J0 [5 N) e
* @return*/. e& o [3 y* f) k! `3 u1 i
@SuppressWarnings("unchecked"), b7 _$ [9 P5 N$ L$ j
public String getString(final String key) {
+ j x/ C+ J, z( i( \: l String value = "";8 j K) r5 k" Q4 T
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();, g( O$ U8 E( K7 F$ w- ?( _
if (valueOperations != null && redisTemplate.hasKey(key)) {
# F% F3 x: M0 p& p/ C6 i/ R final Object object = valueOperations.get(key);6 \+ t, v* x) q. o" n
if (null != object) {. j4 G1 F" D9 f, S
LOGGER.info("--getString--object not empty");
4 i. \! t' N: \2 K- R value = object.toString();. a2 g! N8 v# \
} else {, [3 K' p9 ^% u4 _
LOGGER.info("--getString--object empty");
' z8 s# b) q, \" J }$ k* J; O# \7 |/ R8 z+ C0 S$ S
}
; c" U% u' ~. W return value;
5 \. z& n7 F# C, s3 x7 L2 j/ s }; m1 B: c, ^. v7 `: i% b9 k: R
2.2:在redis中实现时间控制2 n4 B2 I2 _$ p. a( A4 e
7 }6 l; \; E6 B. b; k- h2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。: L) s: n' s1 |" }, }* ?$ F0 U" G+ Y
/ s* G' G$ T7 S% e3 zimport com.youjia.orders.redis.RedisManager;2 H: u% V, P& K
import org.junit.Test;
9 Y& q! m; r! `8 W, r2 ]* i5 I; pimport org.springframework.beans.factory.annotation.Autowired;4 e+ e L) w/ c( ~# X5 j% I. i
1 a' O* H9 E* P/ @. S) z! k
import java.util.Objects;
2 D7 ?* i" \3 z" t! K, `$ [- \7 E- \( i& \: p1 s
/**8 X3 s; I+ g' b( o- A
* @Auther: Yrion$ ~! P1 I& Q/ L6 J5 i
* @Date: 2019-01-11 23:36
2 z, T( F8 k8 D# a */
0 ?: [. e8 o( X0 P, O$ ` E1 ?' z7 X/ R0 b( U9 s
public class RedisTest extends OrderProviderApplicationTests {
+ i" ]# a1 D) L5 a. g& ^/ q$ n7 ], @9 y$ G& h
@Autowired
7 j1 W% `1 k& x9 `8 {& s private RedisManager redisManager;" P! U$ j6 g3 ?$ ^4 `
' k2 i$ [9 V# y! l* ]
@Test' M: x% f* i7 H5 Z: \" E1 c2 _4 E, v
public void test() {( Y0 ^7 ?/ S# f+ F: q5 @: N
controlTime("10000001", 10L);
' }" l- C. ?' N8 H+ \# [ }
6 C) c1 l8 t: _" I6 k2 Z9 S% Y# }& m& s3 T, N" c, r: Y0 I: M) o
public void controlTime(String requestId, Long timeOut) {
# e: u8 U8 u- _7 B! f5 J% s/ W3 y" H/ O4 i
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
) H- Y/ d* G7 `, r% _0 o; ~+ t$ G return;
* b: D8 C0 O7 q G: `( c# ` }
8 R B: r: h2 w) q //something code
8 o/ z; X7 O4 j4 p& \ final String value = "value";0 Y, ?. E8 N, C, I3 p9 L
redisManager.setObject(requestId, value, timeOut);
V9 k8 U) C( @) Y# _) D. F! I final long startTime = System.currentTimeMillis();
0 i& d/ d9 E; H0 f2 X4 r; Z8 I System.out.println("开始控制时间");
; R/ D% t2 u) _ //start
; t; R; ]# z. K4 L* q' f for (; ; ) {2 g! D7 M4 V; K) U! s8 I
if (Objects.isNull(redisManager.getObject(requestId))) {1 j$ K& \1 R! [! S
break;
- l$ [, u4 b3 e( a }
' ]7 c3 P6 S* k. `& l }
3 G" Q) R+ M9 i6 m* k3 L: ]1 @ final long endTime = System.currentTimeMillis();
. X) a. J* ?( e& e# E8 Y8 J8 \
4 i& W2 @2 @! E. o8 z, i final long useTime = endTime - startTime;1 \% @3 T8 G. J, ]
8 n# z2 ^0 J2 G! G0 t- ^7 i# N! I
System.out.println("一共耗费时间:" + useTime);
1 u) H8 g) f4 b( o% d }: h" W1 g! D, {' k8 X6 S
}! H% l% K4 Z! G
outPut:
- E, z& j3 N! M% p- d" \4 M
% }8 K4 p7 m$ m9 N: u8 I开始控制时间* b9 B8 I) Y+ E3 ?/ ^; K# U" j
一共耗费时间:10042! i% m9 [4 m: [. D, L
三:总结" a3 h l; g- R
8 e9 b' Z9 i$ U! p; d8 D本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!( {. ]+ c* y% S- y& u
————————————————
1 x+ R* _/ S5 F9 }6 a" q e版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 d, P; f7 r v4 y8 ?
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
?/ _- G: j1 p; `) z/ h, t( y( V
( l1 J" {9 e) A& n* j |
zan
|