- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 563399 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174243
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
4 n q0 o& n: D% ?& Q Q2 Y
Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
4 k9 v3 [' {! J( C- M% ^9 W" J+ n, t: ^; S
一:时间控制的几种方案
1 ^$ ^' x* i: Z0 [& n9 D- d
0 E9 W! ~: E0 b/ ^) z/ l( x1.1: 从线程方面解决
" r8 S' l$ c9 @- q, Z1 p+ }
& Z1 F, a: E: z, E% t( ?最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
! \! q+ A `% y
# L/ H3 _" K4 M8 M; ? C1.2:使用Timer
A0 F4 r- I+ B- J [" N. @, a; w( V" e* I% J% X
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
) G; S8 X* o. c. m3 H" G2 G+ H5 F( Q( u5 n
public class TimmerTest {! B6 O8 V# f* u) q, o7 D
/**
$ w7 e: k) j( T; m. }$ ^ * 测试方法
" m, r8 I m: T% h$ h. ` */
\, d3 T' C( l, n' G$ t, c1 E public void test() {
# s" p M6 q k" Y* V Timer timer = new Timer();
2 G: l5 z9 ]* z& P! I- M" y+ d/ _ timer.schedule(new MyTask(), 800);
, P' {" f5 m T; v- \ }
: w( V3 ^+ S8 }/ |: p
; Z" i+ L' ?1 d1 Z' H- M# y public class MyTask extends TimerTask {: e1 [# e! @& K0 J% N/ M5 [
! G }- R/ j& I! Z. @9 e O
/**
4 {" _8 n- U/ \0 q* \9 l * 运行方法
" _, Y9 J, n. q7 U7 F1 P& B9 W */
% G+ S% S" y& H9 w7 @# |# Z# u @Override" m* y0 T% B1 a( B! b* z% G, O
public void run() {3 @9 x7 z+ e* L. g" T1 \
System.out.println("输出");/ r0 g" I& s% h a
}. D; w# x# S# g0 T' X
}
7 H0 v/ j$ `4 R! ^% c3 W0 _5 B}
) c. @0 _6 O0 }! x# ]这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。, U: Y+ g* ]2 N! F! _ j. t F
* H" w& _2 s0 T0 W# [' h. @
1.3:redis延时7 t: L# e9 v8 e. d- c8 D. k
, P" p& J8 _* d! o5 ~( v
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:) M2 V H9 E7 v' w& X" ~+ d; V; y
# X2 V8 y3 z9 F' A
# _& M# \- Y/ y. M @ {5 u% `
) C& {+ y: @3 V, Y通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:3 O+ S# H& [- L+ e. F0 }4 m5 ^
- ?* `% S, x, r5 r' n. e5 |" P
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
3 L3 U0 i6 ~' u& ~" z' B! ~. m: {3 t* V; P' K
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
7 g! ]# ~, W: z! l
3 ^. U8 G9 Z3 \( u7 }9 o" h3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
, u- }! z+ C+ a: C; O6 l, B2 g' @
二:redis
* A' b( b" w: E b5 V8 d/ s
1 }7 p" F8 a. o, ~ m/ X) x2.1:maven中引入redis; n8 p4 U6 @& S1 F- R
x7 ]; ?5 Z: o7 x3 d2 ^' w/ w引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。' H C2 W! f3 X, v- h0 w6 w
, u& D& X; G) R
<dependency>
9 a+ v/ R, c* R# {- w9 ^ <groupId>org.springframework.boot</groupId>0 T' s5 b, @, }9 F; O2 E
<artifactId>spring-boot-starter-data-redis</artifactId>4 I* a6 Y/ H0 P% T% {* G+ D
<exclusions>
2 P0 `' l1 _' J$ e& z6 J <exclusion>+ @# c- \4 `& l" B* G7 C
<groupId>io.lettuce</groupId>& Q" ^0 l* @5 B' r" L# w. M* B
<artifactId>lettuce-core</artifactId>5 o$ Q7 h# U( G3 R! j5 ^# O
</exclusion>$ T% k9 X, L) W; n) g# n! h
</exclusions>: d f, c2 {. i
</dependency>! N8 T x$ L; d
<dependency>* k# G1 Z4 c: j* [7 T q
<groupId>redis.clients</groupId>
n7 j; X2 N' g8 C8 d6 R, S. J <artifactId>jedis</artifactId>
' N: b) L+ H( \# f# N, D</dependency>
( |( u) \* E) [ j1 u2.2: 在springboot中配置redis. N1 c' g9 a! @4 k
4 j+ K0 S0 N2 W& l- v( Rimport org.springframework.beans.factory.annotation.Autowired;) _, H, E/ m' `0 H: X$ b
import org.springframework.context.annotation.Bean;
/ e+ R9 R3 `8 s) A$ j/ jimport org.springframework.context.annotation.Configuration;
) l' F# A0 C, b* Cimport org.springframework.data.redis.core.RedisTemplate;2 t/ D3 n4 T, y
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;5 e8 Z9 k# X, e0 t i' ?) C
import org.springframework.data.redis.serializer.StringRedisSerializer;' J$ P* N2 \& v8 \
( `) ~% K# L7 A- {* R) n
@Configuration" L% h& Y( [2 x
public class RedisConfig {- Y2 l1 o" X1 t" n% d
9 S" _! U7 c2 R! C, `2 j
@Autowired
5 H. ]/ Q5 y8 }! J: Q private RedisTemplate redisTemplate;5 n1 H k* |6 r" O
B1 u2 i( d$ K9 |$ ]+ t0 i7 N s /**
) i4 \: y$ L) B- i! x# j * redisTemplate实例化) y( ~# i" E! {% F/ O7 u- o
*
& T; x# ]' E/ b# f9 U * @return8 E& E, S+ O$ S0 d8 g
*/
9 s$ t+ o% w# G* K/ b- C @Bean
$ L0 [: m8 u8 j6 l public RedisTemplate redisTemplateInit() {
2 n4 k1 h; s0 O0 {% A //设置序列化Key的实例化对象
8 ?9 M _0 D( M$ q- q5 o; B redisTemplate.setKeySerializer(new StringRedisSerializer());& q6 H! n! {! K7 C) M& p
//设置序列化Value的实例化对象5 ]6 N8 u# k) l9 l
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
, f. o: G( y- Z6 k4 ?0 x return redisTemplate;2 p7 b6 }) }, e d8 I U! O
}
: ^$ l7 ^$ G5 e n1 P% T9 V, i/ p/ K
}
' g) j& f3 d* O& Z- B. ^' x2.2:redisTemplate模板工具类 x2 G4 V) n2 Y. Z' {
% C1 ~$ l* d3 o3 }
@Component1 w1 x. _+ {- I7 g/ n
public class RedisManager {
3 d' N% } n& @ o r: v
0 d% t. |) X" {1 g( U4 U) W private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
4 ~# }' g8 S$ {/ i# h6 ]7 h+ M% x0 q- B$ q) Q7 L) M0 _8 {4 l
@Autowired5 Z% y# @% @5 }8 i+ x# C7 j; P
private RedisTemplate redisTemplate;
: p0 Y* Y/ I: v; R! x
$ r7 ~0 A" n9 ^8 S8 R3 @! o5 J /**3 }- u1 f7 @: {# }( j$ o) J$ @
* 设置对象
! ^, H7 p% j* X$ C *
# w# p' j" E6 B * @param key key# R) {4 h% |2 F, E2 _) P
* @param value value值
6 E: M+ g& c0 B * @param <T> 返回值泛型
% |, c2 j* O- D5 M* l4 j) m8 K * @return 正确的值:<T> 错误的值:null4 c( k, J, K; y L8 M
*/
3 `) e9 C" v2 ?$ ~- o @SuppressWarnings("unchecked")
% ~, r- v6 [. @0 l2 E( d public <T> ValueOperations<String, T> setObject(final String key, final T value) {. f6 n0 O" z) o2 Q7 N+ x, m- R4 z
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
3 S+ i- m& Z' Z operation.set(key, value);$ B7 [3 ?* ?& q, F- h$ |
return operation;
3 Z8 d! h. E# R7 r }
% X3 v; q0 y) V4 g3 g- R, P- i! ^: ?$ Z$ j# Z4 G
/**
6 ^! t- \& P' ?; _3 c* y * 设置对象及失效时间 (单位:秒)
" [2 G7 z; d" i2 X+ y% v$ Z) r) W *
* R: I3 k7 X- B" w. r * @param key key: u* R7 K! r/ h( [: ~" k
* @param value value值
+ z% v ^3 ~+ v( ] * @param <T> 返回值泛型
5 k" r, e* C2 ~( q/ g * @param time 秒值9 c; J" F8 `1 r/ ~! ^
* @return 正确的值:<T> 错误的值:null
6 H' }2 x- A" Q; e! P1 A */% R$ v9 B8 I& Z
@SuppressWarnings("unchecked")' H g5 W& [ s8 v7 W0 C- j
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {" {7 E# v4 W+ \) l; [! k
final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 T0 `. Q5 Q6 @! B, k
operation.set(key, value, time, TimeUnit.SECONDS);
8 ^; D9 T4 E3 P g# ]4 p return operation;6 S* V- Q' V& P9 | Y; v
}
% }# m" O- O9 {. z! y0 E% f; b% E, \% P2 l5 t% G3 m
& P- O6 \1 B* V /**0 v+ g8 O3 c6 x% y
* 设置对象及失效时间(单位:毫秒)
; V, v; `( W- v$ k, | */ ?0 o1 K) H' V* L8 A
* @param key key% Y1 v5 z% \2 u" h' T6 d2 e
* @param value value值) q0 R+ M' ?2 V( E+ _/ I) a
* @param <T> 返回值泛型3 {3 a/ y$ f5 r& {
* @param time 秒值
4 n2 L1 |; u" H4 \0 D6 V; }0 l * @return 正确的值:<T> 错误的值:null
, F: A, k" [" {, ~ */
9 [% U7 T: F; z, g5 F @SuppressWarnings("unchecked"), r. k; K+ \5 n8 m
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {- Y6 R3 Q, {% x5 ]! x4 [1 H) i6 p5 I
final ValueOperations<String, T> operation = redisTemplate.opsForValue(); A. |% F# }( m) l# e$ T- }
operation.set(key, value, time, TimeUnit.MILLISECONDS);
! `% M8 T! f: O \4 ]+ R return operation;+ U; Y; F9 m/ d% E
}
" V3 ]1 o( a' [% }
: E5 S! U/ e ^" Z' M /**9 h- c3 Z; e- l5 t/ l
* 获取对象
; X2 | D7 O& ]1 ^ Q *. ?2 v6 C- i. S% d7 N x, R) o
* @param key 键* y- N; n# B: E: I3 ^
* @return 正确的值:Object值对象<br>/ [% {3 A5 T2 S
* 错误的值:null" U/ G( `% h, c% C: g
*/' [: P& c9 o+ h `
@SuppressWarnings("unchecked")& ^; b' e; c1 O& I+ Z2 p7 s
public Object getObject(final String key) {7 K' z" \" {9 F9 \
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();! [/ ?& j! {+ t W/ b2 I; s7 V
if (valueOperations == null || !redisTemplate.hasKey(key)) {$ u! \7 ?% R. X3 z8 v+ O
return null;6 n% |9 L2 k; Y+ _: w8 t
}$ f# t6 l) h& t. P
final Object object = valueOperations.get(key);
, F I9 P2 \/ p+ Y& L$ q' t* [ return object;$ T% P0 w( x: q1 N! o, F+ d. F
}
1 m8 [/ W. E3 w1 r2 R
S; T: a: {$ N$ q* I y: g0 o /**: C3 |7 z7 ?8 W* b/ [$ h3 P
* 从缓存中获取string值
, n+ B/ R( O* ~6 m c *
9 N4 {8 M! C2 e! W1 m# d2 S * @param key+ y# F. k1 i! b; Z* |2 I: U
* @return*/ k( z+ U- w8 @7 n1 H3 F/ H6 s
@SuppressWarnings("unchecked")' |0 I. Q. [* F8 A! l1 k# _2 H
public String getString(final String key) {
0 j: R- D' f% {8 R h, H. L String value = "";
# C3 ^+ I) b8 o3 E2 Q% x final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
! j9 D& c5 k6 h* E6 }4 } if (valueOperations != null && redisTemplate.hasKey(key)) {9 H5 Q) l: n% O7 Z
final Object object = valueOperations.get(key);* X3 f _/ q+ i1 {. u) i
if (null != object) {
. |- B% x( P9 i3 S% F4 Z# } LOGGER.info("--getString--object not empty");4 u4 s, P2 N5 n1 r- @4 ~' {
value = object.toString();
8 K q. k q: q3 C8 p8 j0 T6 S8 A) c" h } else {7 ]" O# [) M& o
LOGGER.info("--getString--object empty");) M2 ?" L/ b% A+ g
}5 K8 X0 T2 _7 c1 L0 T V
} C+ w& |6 ?! p8 I: r/ ^' l1 F
return value;3 J8 s* \7 H# ?. k1 ^, p$ w7 L c
}* @/ N! c4 {& J7 `, R
2.2:在redis中实现时间控制% o6 j* l- f. \* D) A5 Q* V: z
% R, n' S, b6 ~& X' l7 t
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。% M5 ?6 W; ]& I: c0 F' W
/ z2 l% O( E! Y6 Qimport com.youjia.orders.redis.RedisManager;
1 Q9 O5 D6 `! ]# G% E/ Wimport org.junit.Test;0 M$ O( O" c9 p" C
import org.springframework.beans.factory.annotation.Autowired;
4 {" e$ i8 l* Y0 B3 Y; V4 v! Z8 p6 o
8 P, a P$ [5 k3 j& a: Zimport java.util.Objects;
) ^( m! q9 N4 r1 Z" a, b7 j/ s" j$ W4 V0 [# K
/**
1 K) L/ j+ \. n * @Auther: Yrion
4 k2 L9 m3 z3 c& G& H * @Date: 2019-01-11 23:363 g" e) X3 R+ X3 {& w6 w
*/
! B( e2 `* S. j+ v9 L
2 k) b" K8 Z0 s' W, U4 upublic class RedisTest extends OrderProviderApplicationTests {
% e6 Q0 R @! g$ W
" j, s1 W* N" {) @! r |+ p @Autowired
$ p0 q, Q2 d; z$ E- P private RedisManager redisManager;3 }1 \+ H5 | k9 n5 \1 {
# n8 _' o5 [+ C) j' e @Test# o+ k) K7 D1 P9 ?4 o
public void test() {
' j* T& ~+ U6 E controlTime("10000001", 10L);/ a5 p# ]: l6 N% y3 m7 U' t1 _. E
}
5 L4 h$ E/ P* g
. B. ~! N0 g* @2 ^6 w public void controlTime(String requestId, Long timeOut) {9 K7 w! D' t- p5 V% _
0 M" {( F: v6 G5 T if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
5 n) {% P p5 i6 l3 \ return;
/ t' g& E5 q- _2 n6 G$ ] }" u5 O2 h% Q2 D" e
//something code" }, q# @9 ]& A
final String value = "value";1 j) O2 ]( N8 E. e) b: y
redisManager.setObject(requestId, value, timeOut);/ Y& g" i8 ]0 c- E; h7 a. H% a, i
final long startTime = System.currentTimeMillis();1 D1 D" q, c: J3 C
System.out.println("开始控制时间");
3 z; C7 @% a6 f0 M //start q* h$ |% `2 A/ g' g
for (; ; ) {1 b8 j9 _% ^' L
if (Objects.isNull(redisManager.getObject(requestId))) {
, r8 O+ V' N, I/ `; M break;6 x, K) @2 [9 I9 A' z
}
9 ^0 B7 c% C8 p) k! \( N4 ] }
8 ?! g- l8 L* d; |) t/ V final long endTime = System.currentTimeMillis();, O0 y+ r6 Q& ~+ d8 h4 B( a
( I- M* w+ L$ d final long useTime = endTime - startTime;
9 j9 A% U* a4 D D5 \+ s: |5 B: C8 `; u) _+ F0 a
System.out.println("一共耗费时间:" + useTime);
, C( x; F* y1 L1 ?. u# [ }3 ~: ~) [ {3 `' P4 l
}$ q# y- I5 ]" _# D' T3 G4 `2 t" u
outPut:7 c/ ]5 L& \' `% J: D
4 V3 X7 M y$ J# @3 }( m7 Y/ e4 Q
开始控制时间
) x0 c5 R# F) Y9 D0 Q一共耗费时间:100422 S: {# _) M- k( @; {
三:总结 c9 g3 p7 a/ R9 m" r
, j% g9 D9 l! |% h9 K* ?7 Y本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!* ^6 N) Y( j/ Q" V0 V5 B
———————————————— ^" R4 w1 N& N0 r. a [
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
/ F/ m- x2 l& l: S$ M原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325; R' p% q! i1 C/ `* {
7 O# ~7 w& O( D$ T
1 _' o# Y% [7 X) v
|
zan
|