- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 566728 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 175241
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
1 m/ Z( ~ E5 }0 j% c: lJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。5 m2 C0 S9 ]# }
; ?) l) P+ d5 I! n* S$ m* }一:时间控制的几种方案
* w% e1 e8 X. c& [% W, J
3 A6 f5 m q! W' @% d8 t- ~1.1: 从线程方面解决* L9 Y: H. \, \: Z9 v4 k- K
0 w2 ~# B5 v k0 G4 l+ _
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。/ v: c7 v, F2 o* I, @
+ L1 s: r+ k. m3 N8 ?8 ]1.2:使用Timer8 g$ I& b( D# n% I+ J: g
7 f5 T$ R- K4 n
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:9 O5 }) {7 I6 w2 k, Z0 E: q/ H7 h
! ~$ x8 l0 d- s5 y* ]# e
public class TimmerTest {* F, s6 n- c( |: O0 q: v
/**& v7 J1 }- L, {- s
* 测试方法
# W4 i& N: _9 B */
+ Y+ ]. [* {) u9 a1 x# P public void test() {1 a X4 p3 |3 K# r# K& [
Timer timer = new Timer();9 I! u4 Q4 V2 j+ c2 @4 [
timer.schedule(new MyTask(), 800);# v. |$ r! V, i2 e. J4 t* q
}
5 d6 l( W/ o R4 T0 R7 q* {8 f- e; P5 U3 Z2 g7 M
public class MyTask extends TimerTask {/ L0 U S/ n3 u
4 w( |# y3 x+ t9 ^ /**8 ^' a# k9 n/ @
* 运行方法2 R/ G0 Y+ U# Y1 e+ F: K
*/
7 i' {( H# T$ n5 u' `2 y @Override* k/ f* Z# A' @4 i
public void run() {: A# m! u' g4 d
System.out.println("输出");! _5 ^5 e5 T: E
}0 ?2 V7 v r! j' e, F- `9 A5 K
}
' B& G1 j) a7 F9 A' ^" ~}. i% d! J* p( e R
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
1 Z- I) L" }+ s
7 K4 Q8 v( p9 m' c9 z$ l, Z" e5 \1.3:redis延时
- G% r e& p: ^* r7 D! H }# W% d8 M" `. U, F; {
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
* T9 H. E& e& u5 y6 m& q# X- B) b$ [, U' b4 \- _
! J: r/ _; j! e3 D4 z% ]7 A7 C8 c
6 r( R) M* X9 t' x$ R通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:+ F( A0 Z6 H+ _% A T- h
4 {# l7 K* }4 C4 r# p
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制% I' G/ S) m0 N- n
3 g" ^4 N/ d% z: E6 N2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现( q% X, W9 X% e$ a" j
) Q+ g3 }" M" W: a& L. h
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
$ x! w6 w- H8 G/ S" ^; k
+ u7 b. [9 }5 G二:redis" \/ {# V" n4 \ k& f1 _
" s$ a1 a$ ?) Y/ K! T2 N$ G
2.1:maven中引入redis
5 F) l. z1 e8 R3 R$ c7 h! c' b3 g, u1 |
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
; r3 |8 L! e, \
% w/ R! t/ J2 {& m<dependency>9 }: y) ?( u( q8 K1 L. ~/ D% C
<groupId>org.springframework.boot</groupId>0 x/ F, ?7 ?6 K/ m9 P" F* e
<artifactId>spring-boot-starter-data-redis</artifactId>8 W5 o1 X* l( D8 v5 {9 K
<exclusions>1 R) y4 H! S1 @- }7 `; `
<exclusion>* ?: o+ o; U# t& L' k5 x F$ S
<groupId>io.lettuce</groupId>9 G# c3 {6 m8 o( U
<artifactId>lettuce-core</artifactId>+ j# l8 L3 F9 z3 Z
</exclusion>- B/ O9 \1 u/ ]( g2 x
</exclusions>* G3 G K4 J c* S" J' u
</dependency>
! A2 k6 T, O) l# [0 P<dependency>0 y. f7 j0 M) `0 R5 j- M8 [# Z
<groupId>redis.clients</groupId>
/ y& m9 ]' i" z1 z <artifactId>jedis</artifactId>! [# ^' l: g) X5 y. f' _) N7 l
</dependency>
! a3 A1 l8 [/ }6 {+ v2.2: 在springboot中配置redis
1 Q$ p! V; B6 A' q% |/ |! q' B: x+ {
import org.springframework.beans.factory.annotation.Autowired;1 D8 Y/ e G9 D) s8 Y
import org.springframework.context.annotation.Bean;
9 q% t7 l, d0 H4 b: I8 eimport org.springframework.context.annotation.Configuration;
* z* w6 I3 }9 X9 Z- m: Mimport org.springframework.data.redis.core.RedisTemplate;
# _+ W+ Y0 g+ m3 @, kimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;: X% W" v1 i1 ^( J/ r- }! D3 Y( U B
import org.springframework.data.redis.serializer.StringRedisSerializer;. L+ S2 T" M8 H
% l9 f/ J. c1 {7 g) B( N! A; a6 e8 n@Configuration
0 p) E! b& }/ A. Mpublic class RedisConfig {
7 Y) T0 S- m# ]* F% ^' l0 j. M* S0 m, v. ]0 f# Y$ f
@Autowired1 q) D9 L9 k+ s! h: y
private RedisTemplate redisTemplate;
/ ], Q8 x5 O; x6 W
5 r x* p$ y% ?% J( H /**4 u3 e8 z7 s/ ~9 D* ]
* redisTemplate实例化8 x0 v- P6 J* @: C# J/ k6 w
*6 n" F8 ]& {* h7 _
* @return% q% g) o) W8 X" j" H
*/
; ^1 O6 ~9 p: C @Bean: e( t s8 o. k: T3 t
public RedisTemplate redisTemplateInit() {
& Q# |/ N- S8 | //设置序列化Key的实例化对象/ X. i) C: _: d6 c a. Q& K4 |. U
redisTemplate.setKeySerializer(new StringRedisSerializer());
& J! x. ]1 a, J2 L3 T4 \ //设置序列化Value的实例化对象
! F+ p: v- [9 ?! W5 Q! I redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());* C# Q( a: V1 o9 s" {- Y6 @
return redisTemplate;2 B0 b3 l" v( t ^
}
1 P0 t2 i6 i0 V% n2 R0 c
4 r# C7 ~* J. C1 w} U! s" Y3 f' O0 Y* ~
2.2:redisTemplate模板工具类7 [8 R- x% N0 n& I1 @
- r# G0 ~8 @; q4 }! N" q@Component
: D% V5 L+ t+ D8 a S4 M+ v9 Rpublic class RedisManager {
$ G- l( Z: v. S3 F" W9 {
5 q/ S7 E( O1 W. p. w' f) q private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);) X2 U3 U) [+ V- O$ t4 J
# N- V4 h7 j% S0 E* n
@Autowired
5 s- s8 P" b. {8 I private RedisTemplate redisTemplate;7 E; s5 a! e, p' h1 U) }. b
' l0 n" w7 S* t# `1 b
/**% l: g/ j) Y1 e3 F' L! }3 t" W3 Y
* 设置对象' S; G: Q+ L" K3 ~, Q
*
4 D( ^! ~8 A$ _( q * @param key key) Y3 W4 J- C4 t( T0 w2 `- L1 o" B
* @param value value值# k0 C; U" Z: Y
* @param <T> 返回值泛型
* k7 Y# B; D: X+ L" K7 ` * @return 正确的值:<T> 错误的值:null
+ h9 r$ Y0 A o( R */
9 ~' @$ ^/ D& M @SuppressWarnings("unchecked")1 w0 J6 c* g4 T* r, Q4 f
public <T> ValueOperations<String, T> setObject(final String key, final T value) {+ S- A5 d0 [! z! m/ F
final ValueOperations<String, T> operation = redisTemplate.opsForValue();, T1 ?* M H3 u6 \: f9 Q# `
operation.set(key, value);
: ?. d. W7 }9 K6 C3 ^ return operation;
8 N8 @6 Q, b( e* Y" q* ~ }
% Y% h, a* A9 @9 s9 O5 C P* Q2 q/ X. D- t2 X( S+ Y- G5 u# B
/**7 E1 H* u+ o7 L# H! s
* 设置对象及失效时间 (单位:秒); p$ A# ?# U$ H' x+ F" l
*
# V! r& Y4 u+ y0 r. J& a& w; g * @param key key; {5 h; |! J$ ?- e* Q4 o% `( w9 h
* @param value value值
' s. w: ]0 o6 ~- _& p * @param <T> 返回值泛型
' Y) @, C. F5 ~! } * @param time 秒值
& A& A, y+ A% J/ Q * @return 正确的值:<T> 错误的值:null
~+ x& w4 g+ ]7 R */
3 T8 z) {4 e' W* O9 b) f- H& {+ c3 A @SuppressWarnings("unchecked")
3 ?. u& r) B9 ]# ] public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
# f6 g& z: O7 b7 W0 {6 ~- z4 m final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 K" Q f# o$ n$ g* m+ w( y7 M
operation.set(key, value, time, TimeUnit.SECONDS);% F+ S2 \$ ] i
return operation;8 i; `! H5 H& ]' g+ Y1 y$ u2 u
}
( A( U3 r7 ]" m: L% k
6 W6 m0 G! q" e8 w0 f" O& ^3 b; M" V7 I( y ?6 [, Y
/**
7 |( e- t( W' A2 [( @" c/ W * 设置对象及失效时间(单位:毫秒)- ^4 d3 Y7 w5 Y6 D) M; M
*
5 A. t' t' j/ ~1 q, J/ w# z# n4 v% J * @param key key$ y e9 m, x* p: ^% } Y; w. J c
* @param value value值
3 E2 T- x% i% q9 k2 K * @param <T> 返回值泛型" }7 \+ |& j9 @. B. E& H
* @param time 秒值
6 U8 c4 v3 i9 `5 U * @return 正确的值:<T> 错误的值:null W6 |0 `3 E! l/ m: n( Q
*/, _: O% p7 ^" x% k- o g, n) o' N
@SuppressWarnings("unchecked")- [; u0 Z- A( Y( Z( L9 z! b' s- K
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {* Q" a' |1 U8 y2 \7 P9 a
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
+ \, a4 q/ S, ?4 u& Q! \9 y4 } operation.set(key, value, time, TimeUnit.MILLISECONDS);
4 S, e0 U/ m& d return operation;
: \- m1 E( v/ T! k6 D; w3 j( J }( l b8 M+ K; ^( N6 T' O# W. ]
3 p/ X% k, m4 O /**9 y# t; }. I0 S
* 获取对象
2 X0 Z X# F4 Y3 ]8 q5 u5 T *+ M0 g, E3 Z) T+ ~: R
* @param key 键6 e4 E* Q' n( m0 H8 z# l$ |: y9 _
* @return 正确的值:Object值对象<br>
& Z' ]. _' d: | * 错误的值:null" x" s' `3 Y4 o! B$ j
*/ w G; @& D: b y9 q' P
@SuppressWarnings("unchecked")" B7 k9 f2 Q6 G, i
public Object getObject(final String key) {8 o( W: Y2 R( g3 N
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();3 q t/ I8 E; R$ }
if (valueOperations == null || !redisTemplate.hasKey(key)) {1 z6 u# U: c4 e. h/ J# ?: a
return null;6 \ P' X9 P' t4 i4 T2 ]6 |/ M
}
/ z7 O' d! ]5 L6 i8 j final Object object = valueOperations.get(key);; L [0 A( r1 h- C1 u$ J; n
return object;
D2 U5 F5 ] q! U% h' A: H' Z( R }7 D8 E- G5 [; L* `9 E/ A: @0 m/ u' c
/ q2 Q) V. p/ j' j+ f, R9 J
/**
) N# ~9 g2 D y' A. u6 `$ I * 从缓存中获取string值- k5 b8 s6 X/ m
*, d {, r- F2 B
* @param key$ N, L( M; C+ o* A
* @return*/
% G9 v' k; ^- a! O @SuppressWarnings("unchecked")
* G# c7 A$ r/ v5 W& ] public String getString(final String key) {# g( O2 `) _+ R; V+ E
String value = "";
2 C- C+ f3 Y! \4 D7 m final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
7 W; E) c i! ]( P$ M" k5 S if (valueOperations != null && redisTemplate.hasKey(key)) {
) }- [2 ^# U. t+ J# e9 f" X final Object object = valueOperations.get(key);9 Y- }+ W' V" R7 e
if (null != object) {, `2 K8 E {; C! M
LOGGER.info("--getString--object not empty");
2 T' U; z' x. ?! n2 L. t& l value = object.toString();
& h. |# d; A) f' e+ w- ] } else {3 V( s6 C* H' J8 R
LOGGER.info("--getString--object empty");
" X% k6 i& @8 l, N1 {8 M }
! R, i! C3 R) B% [% q; T9 F$ X }0 A$ h1 i3 y% b7 z2 W+ D: t
return value;7 j4 }/ l. S! Q, E9 k$ v! I/ [4 P/ y
}. S" F! [! t# A4 y$ ] C
2.2:在redis中实现时间控制
0 u# H- k5 Q1 M, l' O$ J( _9 T& b% }8 ?6 X# A. W
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。! K" o' s+ B; d2 e' C
) N2 ~) z& u4 t9 D/ N9 }4 d( v! dimport com.youjia.orders.redis.RedisManager;
2 S6 C- V$ J- Q% Y6 ?4 himport org.junit.Test;* N x, C: g7 S1 d2 x: Z+ p" ^
import org.springframework.beans.factory.annotation.Autowired;9 C. |! Y$ W% I+ R: m8 g* N) C
9 u) E* E* }5 \: simport java.util.Objects;& O; O3 b0 S ~' b# X
; b" L" q+ e1 j1 h* o( Y/**
$ [7 V7 e) q% V$ l8 j. b8 g1 e * @Auther: Yrion0 M% x6 f7 m7 f2 |; J7 K# \, }( i
* @Date: 2019-01-11 23:36
' L$ I4 {0 ~+ E: h) P */
- V! ^; f4 r% x6 r; Z4 e" {/ o. r* f+ p) P' h
public class RedisTest extends OrderProviderApplicationTests {
" C9 L; n U) j: ^- o( G: [& P* \& ~/ N. @- K0 S
@Autowired
/ [/ B1 ^5 |. H1 V @: c private RedisManager redisManager;
5 g" _! Q& @% h6 D( d( C
* ~; U: U- i. j" r/ `2 v& L @Test
5 i) K" S* l* e/ f5 A$ x: f J/ k public void test() { X; ~- ?' H3 B/ g' w: z4 q/ ~5 m
controlTime("10000001", 10L);
8 [: r( X5 ]4 i( {! s6 O2 k }" P, e% q1 i# V/ x
7 Y+ E) B5 L0 q
public void controlTime(String requestId, Long timeOut) {
" F8 F! f- ]" F: s+ ^2 J: X) B5 Q; V5 T& Q( o/ g
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
6 s) w+ w' a" C" r1 X2 M, l/ N* v, [ return;
0 u/ k6 \6 f8 b! h! k$ h% h: _- u }8 `* K G2 ?9 F8 V) A
//something code$ i3 B) K9 v9 Y2 ~1 r: y
final String value = "value";
: O% {( ]% J; o% z) o! F redisManager.setObject(requestId, value, timeOut);( L# T2 Q% v- h& e) s
final long startTime = System.currentTimeMillis();- e) k1 D' {7 Z; M) h& @5 s8 q
System.out.println("开始控制时间");
5 {) f1 F9 \) h9 M y# x //start) l0 a/ f @( O- U. x
for (; ; ) {2 Q4 g5 B$ B! N, T% R
if (Objects.isNull(redisManager.getObject(requestId))) {- l$ B( I( s; Z }$ v) E- }
break;
0 Q% E7 W# R( K+ n* E/ d2 o/ } }
, R! z: h. O/ f3 g, q' }' e }
% @% z8 D. E+ \+ k6 L final long endTime = System.currentTimeMillis();
6 d1 c Q6 o- m. V
+ h$ e# c: d0 B7 D, }, G- Q4 m9 m final long useTime = endTime - startTime;
6 w1 z# K7 I: }7 S- V: E+ t
6 w1 D7 z2 S7 U- X/ c- V System.out.println("一共耗费时间:" + useTime);7 `1 E5 y0 E# U- [/ s( X3 o8 w
}
% D$ g2 N8 x9 p4 c: q7 o# j' c" Y}
. _+ G @5 O$ OoutPut:
: \3 c7 h/ U4 T$ A# {
$ l/ M, R5 |& K( j& v' O1 p开始控制时间
) w3 R* a) X3 a" Q$ k j% K一共耗费时间:10042
* s4 j4 p b) I- U+ j- j三:总结6 a! K3 ]! q7 _
- s) }" l7 h3 B+ d& y v) i# \
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!+ l! \+ X( S5 b& D- ]6 c4 a: k
————————————————
E. E( {0 X, B# [# S% |& W8 V版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
7 G% t% D3 c: W0 u原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933255 m# Q3 I( @- _! |9 {
+ p/ {+ s$ ^0 u9 c3 J
P2 e$ z# A; x( Q |
zan
|