- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 555597 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 172052
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 18
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
, Y+ k+ w# ^" }2 t" z) g, dJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
5 k, S+ }- q' c* L% B) w0 v n$ y9 ] o# _
一:时间控制的几种方案 i' H0 v" g/ b# H( S( h
$ T8 o0 {7 g; M o3 C, B
1.1: 从线程方面解决
) i8 o3 k% U# x! Z2 R
6 h& C& v/ i& O8 p2 z' E; K最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
7 Z7 O( }; ~; q% @8 g5 X! u
) Y0 M! G3 y7 R/ x1.2:使用Timer
?& H8 U K7 M4 c% {: v: l, i; u8 b, B' C9 i
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务: H' n+ ]' q! Z5 x/ S" w- y) o; D
8 V! V: D. Z" v, j3 }/ Spublic class TimmerTest {
- x2 {" {/ g% R4 j /**) N$ R7 k$ E- d8 n2 A) h2 R1 h+ C
* 测试方法
5 X0 `0 g& a& z% F */
6 J. A6 l5 Q8 l' L! N, \* F( R4 d. U O public void test() {; G; f. ?! L; @, b2 a( j
Timer timer = new Timer();+ M) t: `9 V3 d! R1 N+ L( ?* j5 p
timer.schedule(new MyTask(), 800);
" |' O, r% @: w8 z R! _; i; f }9 L0 ?* M# ?8 v
; v' A7 \7 u; O, U3 F% u; P6 k" g public class MyTask extends TimerTask {
z; e/ S! k4 Y& l" F/ B& e! z* q* `2 u
) i) p. N( o: A) [ /**% p! C8 A) c( i- c( D
* 运行方法
! ~- }5 ~. x0 Y) _' ^( x. Y! E& Z */
, E/ }1 a* M4 ?0 q$ I @Override
8 z8 @0 P. E0 i* o public void run() {% M6 s* R; Q( [0 U" g3 h# ]6 Q
System.out.println("输出");: U7 j& |1 g* B
}9 D9 ?8 W; E6 w: Y& A" s2 J
}
8 h" S3 a( v9 s7 T* g& M2 j6 o9 v7 m}# M8 ^) Y- u' ?% [& z' p
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
, A/ m- z- g3 C* z& n; i7 W6 J+ U7 l# s6 |6 | k7 K
1.3:redis延时9 S5 D6 i. H) S% C* Z+ K
( b% M. S9 u1 J, e& n0 J在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:2 C$ e' X, Z3 o. \6 j
) Z* G" Q# n6 `- F
3 a6 g8 }& K3 X- @' E0 c+ Z* n9 ^. @+ o; j& i; \3 O
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
4 P! f& A3 E' U1 W6 R4 f, U
' G9 p7 d7 U3 d3 z1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
_) L% _7 s6 ^
6 l5 B% s+ @) `, M6 A' D4 k2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现' q6 k5 @0 T! ?0 I& g) y
* q9 D# O$ F1 {& U# G3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
. p$ m, o3 i1 V8 K9 `. v
, Y; g) f! a1 w, }4 W0 A二:redis
v* k% D% v; M- W$ F! s9 l# i9 ]0 v6 a* N( m- E4 k& d
2.1:maven中引入redis) J8 ^- l4 y N3 o5 y1 h9 d
6 \9 @* W: z& {5 w
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。 E- g7 i8 G: w6 y, @/ `
8 S9 o( y, G8 j2 L" M
<dependency>
: ^& M: p' x- \5 H0 n6 C <groupId>org.springframework.boot</groupId>2 f' G2 ], D3 x' G4 W, Q9 X
<artifactId>spring-boot-starter-data-redis</artifactId>
+ M: {7 E6 s% z6 y <exclusions>
5 O `2 w3 R4 J5 z! y0 d <exclusion>
: H; _& W" H, s8 x, O% L <groupId>io.lettuce</groupId>
+ z: U$ g" {, u4 o6 { t+ { <artifactId>lettuce-core</artifactId>3 H% n" B2 t k: _
</exclusion>
G8 }; V8 f1 p+ k( O </exclusions>8 Q! @& Z% a$ s
</dependency>
4 I% T+ I+ S; x3 |* P2 x/ x: x% T<dependency>
. D5 a+ O" i) P# K: t% r <groupId>redis.clients</groupId>- h5 E, t$ u8 N5 F+ t A% \6 d
<artifactId>jedis</artifactId>$ d' s2 a( q5 B1 ?2 I$ ?
</dependency>4 E, V; {% x7 ~+ s
2.2: 在springboot中配置redis7 b: r, q. {+ }8 j4 F
C" }$ o4 \# R. z
import org.springframework.beans.factory.annotation.Autowired;2 u+ z4 L0 L# p, z) ?9 y
import org.springframework.context.annotation.Bean;
' w7 h8 @2 i9 ]8 c3 d" Cimport org.springframework.context.annotation.Configuration;
, A) t2 s: u r: j! _3 I# nimport org.springframework.data.redis.core.RedisTemplate;+ F. X$ B7 |. s, Z9 q( c$ P
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;9 y; L- t5 p0 J
import org.springframework.data.redis.serializer.StringRedisSerializer;
: u/ H3 |- [5 j4 U5 q' K( k
" Y: I4 X2 `& ~ Y H; }@Configuration
. w% i# i1 C% U- _6 b- W `public class RedisConfig {+ y2 |/ j0 U- x9 Z! k9 @
$ M1 A8 `8 ~. G, X @Autowired( X, l9 C9 f7 b9 V
private RedisTemplate redisTemplate;
) {. f, r; w& I# B9 t" X% a
+ U4 o7 K K6 P. D: x /**3 w. N0 i8 H' e8 h' P% C+ k
* redisTemplate实例化8 i' I' X+ \* W6 M3 N
*
+ @' Y6 M! F* h' I0 p * @return0 k3 _8 p$ U& b$ b/ b0 r
*/' s+ z3 ?- N( q/ ?+ e1 B
@Bean$ X+ h0 D5 n& T5 P( e* C" y
public RedisTemplate redisTemplateInit() {. |; \3 y) b& w1 `1 d/ d* ?
//设置序列化Key的实例化对象
% \/ ~- c) h! o4 r' h" X1 o redisTemplate.setKeySerializer(new StringRedisSerializer());
4 z& x3 R6 x. Y3 R" F( z4 j# C* o$ ? //设置序列化Value的实例化对象
8 U' y2 f! Q- }- [9 B redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());' Z. M4 T0 ]) P% O6 Y- S' ?
return redisTemplate;
: I {4 X) L. ?+ K6 C" h: y }- M: \! Y: Z& N# y O
9 g; v9 U' r# U Z, b3 |}
5 ?5 Q3 N z7 F3 R) p/ p( I/ X) w2.2:redisTemplate模板工具类3 K9 }8 X) E% W3 Z
* k2 A/ o9 K) ?* e" O6 Y! r@Component
: D% U0 z* K: {+ M/ B ^& Zpublic class RedisManager {
. X/ d/ J: H5 L- j' z X& Q/ e1 O: G
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
* F6 L% n @/ n$ `6 l2 \7 I
/ ?, e! I- k* a# `8 }2 j/ `4 ^ @Autowired
6 E6 _+ A0 w" b+ t private RedisTemplate redisTemplate;
( P" e% }: v7 y9 q a
' U2 l7 t Z' M7 ]( J5 c; Y( M" ]3 a /**
% j! k7 k1 \$ M: H * 设置对象
! V/ h' a8 P- A k; t \ *9 Y4 L7 G9 D2 c( g- ?4 x
* @param key key
0 C! j' R$ m$ m( m" d * @param value value值9 c/ n, U. ?, r
* @param <T> 返回值泛型4 K+ {2 t" l" {" M3 a* T7 o% I
* @return 正确的值:<T> 错误的值:null
+ S2 r* Q; Z3 r) q5 ?8 ^. }, Y; C" v */ U: i* |: W, x" f, k5 e; u
@SuppressWarnings("unchecked")9 ]& j& _" M8 ]# P- X q. ]
public <T> ValueOperations<String, T> setObject(final String key, final T value) {' [5 S- y! S! G: \
final ValueOperations<String, T> operation = redisTemplate.opsForValue();; @8 e+ e, j, Z
operation.set(key, value);- E# I! L$ d/ Y1 j8 ^) S K0 L; }& P
return operation;" n; w: h- H& t ]1 h( C
}
* j8 Y+ |( F5 j5 J+ q
" }6 ?& N/ u$ i- ^! t) i /**
) }1 m$ l8 d$ C( O) x0 o * 设置对象及失效时间 (单位:秒)
, [- b1 Z7 L; Z *
) ~' [( o! Y) B G r * @param key key7 K: _ B3 k+ P3 I3 p0 O
* @param value value值
# U) c6 [8 f3 c6 ?0 J, V) B9 ^% s * @param <T> 返回值泛型
5 O2 P$ s: L8 z1 n: s7 Z* }; E * @param time 秒值2 f9 X; v5 w* x7 I6 r
* @return 正确的值:<T> 错误的值:null B8 A8 f9 C9 X3 x
*/ g0 Q7 ^8 r0 Z; E
@SuppressWarnings("unchecked")# I8 V' ~% W% H; F9 A4 u
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
3 _- {% \1 f% z- Q% X9 }: F' {1 V0 a final ValueOperations<String, T> operation = redisTemplate.opsForValue();6 ]( F( o& n2 K# U- ~4 G; C
operation.set(key, value, time, TimeUnit.SECONDS);( {: y% ~6 ?! U5 d) o
return operation;, {. T1 }& ?4 q) Q" a
}
' d! A8 j. r0 F( f8 U8 g5 w0 [7 Y. N/ q
( a& N" t- k$ { /**
. y( j' H" A( x# i, B( o * 设置对象及失效时间(单位:毫秒)
" z; i" x! P' y2 T' M; ?' E2 t7 ~ *
2 j' b4 K( K$ M' I9 ~. u9 i3 j * @param key key
0 ? |- w5 x8 H+ ~! l" \ * @param value value值* }2 @ a+ H& I: `2 U* e
* @param <T> 返回值泛型1 X$ G( ^) x( t0 E' ~' V
* @param time 秒值* ~/ K; |% p! J+ s% t+ x2 v0 q
* @return 正确的值:<T> 错误的值:null
& K' y! N- ?+ [: q* p8 F; k8 U */
' I' }$ ?5 s# s. a7 `9 c @SuppressWarnings("unchecked")
O, g4 t) C- ~ public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
" m5 J) ]" d; {6 q0 _ final ValueOperations<String, T> operation = redisTemplate.opsForValue();
8 U3 b4 D* y- w" I: e operation.set(key, value, time, TimeUnit.MILLISECONDS); N1 i: v9 N2 o& R& v
return operation;6 v2 N' S. c0 J" |0 i
} s3 I3 o8 Z' ]% |4 l
$ |5 L: u) H6 [) c! T; k; L/ g1 l /**- j0 t7 V6 f# h+ y/ x, i* o4 E
* 获取对象! y7 `4 u7 v4 f" j. w' |1 a2 z6 ?
*' H. N1 N4 c# P. |# c
* @param key 键0 \ n5 K/ `. \/ B
* @return 正确的值:Object值对象<br>
" ]8 `) D, ]7 J! a4 N * 错误的值:null6 k) ] F4 J7 z3 v2 a
*/$ @ H, E2 d! z
@SuppressWarnings("unchecked")2 a! X" F0 h2 F: o0 q. Q
public Object getObject(final String key) {7 e, P( v6 p: Y$ l, p# Y- k
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();! J/ D' w* @; S; O3 _- R# g9 K' a( [, W3 g
if (valueOperations == null || !redisTemplate.hasKey(key)) {
" j+ }3 Y. r& J [1 k2 T return null;
' ?0 }; f {1 y, T$ ~8 W9 A }
# g1 C1 n7 p: u2 z. X" L5 D final Object object = valueOperations.get(key);
* p! J% I' J; f return object;) s, d9 M* C) k ?
}
( D: Y% R7 z) t1 P7 g3 K; X O
. \0 {6 a8 d* h0 m( b /**
: D7 S8 o8 g; I$ f0 M8 t, Y * 从缓存中获取string值7 j; D+ F8 E5 Y5 _& L/ {9 e
*7 M- {4 ~0 w" V0 N# y
* @param key0 ?& Q+ w6 ?, ?+ k ~
* @return*/- f3 g5 e5 V8 k1 p e! `) [
@SuppressWarnings("unchecked")
3 ~$ E' h- M4 }7 [) Q: z) k public String getString(final String key) {
. ?4 X& g0 ^% k0 | String value = "";
/ _/ }- `9 t2 E8 l. l4 Q |9 I" }$ q7 e3 I final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();3 e, ~' {' o+ G" F: |- r1 u
if (valueOperations != null && redisTemplate.hasKey(key)) {7 P" z' P1 O" q/ G6 F% ?
final Object object = valueOperations.get(key);3 K! ~# T3 p9 ]. h" u4 d. u* b4 e
if (null != object) {
3 X" B; X3 x7 c4 a5 z; ^/ q% ~7 b LOGGER.info("--getString--object not empty");
" P% S& a! X: P value = object.toString();
^4 V9 a" G. w1 U% ~# g } else {6 V) g9 K1 E N
LOGGER.info("--getString--object empty");
6 t W1 D. n# I; Z0 I9 X6 M }* k |, T8 p2 v5 `! C
}& s& E6 T/ C; \" u# L% a
return value;
; y6 O$ q9 J8 u6 O6 E& u& d }
3 M# G4 X! o6 ]$ v7 v% q" x+ K2.2:在redis中实现时间控制
# o3 R: W' K/ K# r0 u' ~/ N6 Z- Z6 ~2 e1 `
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。- Q5 ?1 X; W; x7 E0 r
* _" P6 @9 D6 u/ {$ zimport com.youjia.orders.redis.RedisManager;% F7 A% R1 h& L; h; z
import org.junit.Test;- I% g9 X$ W2 b& d0 i. l" Y
import org.springframework.beans.factory.annotation.Autowired;* X) _% R9 }/ J, `
6 T. X f- Z! e/ |1 Y' Y0 e7 M
import java.util.Objects;) D L$ Q# H1 ]+ K+ U4 ]! g* o0 ?
( c8 M+ e. U) j6 z( n% ~/**# c0 ~( H* p( t# q, B, p
* @Auther: Yrion
' a+ f7 h: L4 L% z+ _6 E * @Date: 2019-01-11 23:36
$ r, o u" \/ v */
U( k5 L Y$ m4 f) @' L. a, I. \7 k' g- [
public class RedisTest extends OrderProviderApplicationTests {
& ^: S0 Y0 s/ @) }6 Y6 b! M$ t0 ?- M* `3 G8 U; i/ Z2 }- n
@Autowired
- L3 K) E, J) Q private RedisManager redisManager;# T" n( U/ B$ \5 A- L
* u" p1 J5 i4 p f- a% k) e+ J% y/ S4 l. _ @Test& J% N1 d1 h0 @
public void test() {0 F1 M: d: q S( h/ e
controlTime("10000001", 10L);
7 A4 C) y+ p/ r: z+ G) w0 w }* t2 r3 c2 Y0 d, o5 R, Z
7 g* J$ X2 s+ f& [3 H( W public void controlTime(String requestId, Long timeOut) {. L" S& H, B3 O5 p8 k
/ ?8 [! ]8 C: g$ b( k/ P1 ?; E
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {, w5 y$ D! P8 n# ^( k) X! u' o: d
return;
0 S7 Y6 I6 l, g8 l+ |. d }
- c% T- n) e! ^. E3 a* U4 D6 t' Z //something code$ L" r+ t- O* `" ~* x
final String value = "value";
! E$ {9 g4 O6 \# L1 X2 ?: h5 b redisManager.setObject(requestId, value, timeOut);6 Q+ Z# y$ w/ r0 q
final long startTime = System.currentTimeMillis();
. c2 o8 m. _2 w: T p) n$ Z System.out.println("开始控制时间");" b- C0 b( P" z- y
//start
9 `" N# \0 |2 A0 G5 l1 u for (; ; ) {
9 i6 P/ O( A' v2 ]* D if (Objects.isNull(redisManager.getObject(requestId))) {
* `( S% Z0 `( c2 y0 ?) E break;
1 [" h+ [! P- u" L9 |( m( I }
) n, T4 v7 P+ P& R# l1 M }0 i3 x; K5 R, b# N$ W/ \
final long endTime = System.currentTimeMillis();7 t5 j* M! ^% o) _0 m
( F, B A' n" @: \7 u. ~1 h& S/ ~ final long useTime = endTime - startTime;
/ S4 [ V6 V1 U" C# A: f
7 o9 B2 ?" L9 x" O System.out.println("一共耗费时间:" + useTime);8 M9 a9 s; R# S: F3 s; c
}
) \8 P$ Z2 Y9 {3 x' W' n6 }% |}
2 t" g _/ _1 t- ]) @outPut:7 ~! d5 ~1 ?' l, `$ G$ w& e7 X, ~
- X+ d( l' m" V% `
开始控制时间
8 e C0 k+ z6 c5 F- @& @9 I! k1 q0 j+ C一共耗费时间:10042/ }- C( W7 @& `- E" W& E5 T+ a% @
三:总结2 N- S H2 P4 e/ u# k8 E
& r6 |4 _. b/ S* [- s% e) X$ d$ m
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!/ [9 ^1 j1 ]: W3 W
————————————————5 O; D- o' {3 g
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
: s' X& S# e5 e ?/ \; w4 X原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325: Y$ n) N) z' _+ @9 z7 Q1 o5 V- u) V
. e7 Y( f) o W6 M# L) I3 E2 A& a+ S
|
zan
|