在线时间 1630 小时 最后登录 2024-1-29 注册时间 2017-5-16 听众数 82 收听数 1 能力 120 分 体力 565783 点 威望 12 点 阅读权限 255 积分 174958 相册 1 日志 0 记录 0 帖子 5313 主题 5273 精华 3 分享 0 好友 163
TA的每日心情 开心 2021-8-11 17:59
签到天数: 17 天
[LV.4]偶尔看看III
网络挑战赛参赛者
网络挑战赛参赛者
自我介绍 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
群组 : 2018美赛大象算法课程
群组 : 2018美赛护航培训课程
群组 : 2019年 数学中国站长建
群组 : 2019年数据分析师课程
群组 : 2018年大象老师国赛优
4 t+ S" Q; R; S) D r Java如何优雅的实现时间控制 前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。3 o1 ]% j, G5 r" }
6 }4 |- [. B, \# {
一:时间控制的几种方案
2 G/ ^" O0 c2 `+ t s+ A% J' u6 C. l . U" @- B! H2 Z+ Q
1.1: 从线程方面解决
0 A) y% u& a: R# ~. b ' L" m0 G0 t5 R/ ~
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。) C7 a7 p6 s5 Z- }6 H6 P" u, v
( O5 q$ M! K( ^/ V0 K
1.2:使用Timer& j" C9 \6 t8 s z6 ]8 Z6 L
" e. e& @: b! W; w 查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:7 @) F7 v+ p/ O R
$ v4 {( N5 O7 w( `5 Z% x. h public class TimmerTest {
' P7 g' Y2 u4 K$ s% p /**2 g8 \' ?2 a; [6 Z! ~9 L! `
* 测试方法+ Y) {& C) S n: [- |
*/; K2 W8 q6 s9 O5 e% _0 x6 w$ ]
public void test() {! j% M" C* Q! n# g1 K# w: M: \, ?0 |/ O
Timer timer = new Timer();! k/ F0 q9 e A/ M' X) I
timer.schedule(new MyTask(), 800);
+ f( K( i9 ]+ K) S# S }8 H* J8 ~9 |: v8 w2 q: H. f) Y- `- t
2 }3 j& W3 A8 |
public class MyTask extends TimerTask {
6 {8 x6 ^, X/ }0 p' D9 _ 0 l/ q) z; S( X: c) N4 F" y
/**
9 H+ b6 j( R+ h2 H * 运行方法
0 W! b& y8 F6 L# @! [/ ^ */) k5 v7 T6 @: O
@Override8 \6 u% W: y- R
public void run() {
) O- h) X0 K3 _6 l) F System.out.println("输出");8 p: E0 K6 }3 p% _
}
& U2 K a( T7 d& f& V# n. `7 H }
' d- G2 g0 a# n; |5 w }
, Z/ {( Z! X6 J r7 M% S7 j 这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
! Y7 H& Q$ H+ o$ j* H* U0 T ( y* z }0 e; q: z
1.3:redis延时! \( T6 \; f Y
5 h* z4 s. J' t- w: i# m( e 在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:9 }! y( O' X N8 s* k1 r' g, y
; A9 |9 r" f: b$ F8 F" G
% B9 B# v+ t+ `
8 H8 B) f* t7 @) f# q 通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:# T6 K( E" U0 d* [
% p3 e" v n* J% q+ c1 m4 u 1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制# P6 G" c# F7 k1 k }
8 z* g0 u( W! G# ]- L; a+ V* F 2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现- {) i% I Q( V- J7 O* t. k9 T
0 d1 o5 j9 M4 K- G2 n- @% S, `7 s) L* J 3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
4 D4 e: C e7 |+ N6 Y& K 5 a) T; Y q- j" P$ M7 c
二:redis
& Q# y4 c, h6 c _0 V. {! F( a
4 r$ z! c! ^3 p' _8 ` I& B4 g 2.1:maven中引入redis! Q3 u4 R, E% V* r9 P7 i) k: s
9 e- k: D2 V- T) v9 x' ?! N) m. O 引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。; X% S" Y& i2 e7 _1 V+ b- C% } \
1 y% }, i3 h @& ?9 { { <dependency>% S% G h( A6 P6 T
<groupId>org.springframework.boot</groupId>
+ e/ A+ S* y$ y5 _ <artifactId>spring-boot-starter-data-redis</artifactId>! c/ R* E5 U e$ t7 F& M; x
<exclusions>
( T' O$ {& K" \' [7 x% | <exclusion>! u" ~% J. j& X) W
<groupId>io.lettuce</groupId>' W0 ^+ P4 V) a* I$ m4 L& b ^
<artifactId>lettuce-core</artifactId>
' x3 p8 f, X3 s% O- x: Y/ q1 T </exclusion>/ _1 t+ O9 S: d* S) k, z( ]9 S: c
</exclusions>
. g+ a2 W, b+ K4 Y </dependency>
2 _( n/ o: S0 R: p# ?, _' u; Z <dependency>+ N0 a, ^* ^9 Q: R0 U
<groupId>redis.clients</groupId>8 J2 |& ?+ N% b3 E8 k
<artifactId>jedis</artifactId>
* y" W# W& ?7 Y2 x4 R$ n/ g! D </dependency> D1 C/ v) _8 b: e+ S( u/ U
2.2: 在springboot中配置redis
7 F. `: e$ \' z8 g
" |" x6 Y3 o; ?/ d& l import org.springframework.beans.factory.annotation.Autowired;5 ]5 T! w b% e8 C8 ^" L. A
import org.springframework.context.annotation.Bean;
4 [1 T# |/ x, s+ x. x+ X2 |+ T* Z import org.springframework.context.annotation.Configuration;. x+ @5 j! @( M; c) T
import org.springframework.data.redis.core.RedisTemplate;
$ R- Z2 f u$ ^" Z: E$ o import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
% G) C1 g2 l* n5 Y* c0 _ import org.springframework.data.redis.serializer.StringRedisSerializer;
, r! m* n2 c9 S/ R1 a4 p 3 K- N; ]4 {6 f( y+ ^ F& K
@Configuration. ^; c2 }& i5 P& K
public class RedisConfig {
3 e6 `4 {' P( P/ l$ Y% Q8 A 4 \; K! I$ }. t* n1 F
@Autowired2 ^& @* |0 K' m8 _
private RedisTemplate redisTemplate;
/ l: q) Q1 b& M: h, K8 f& T ?0 g n( v; Y5 v3 [( Z
/**
" A0 L M) l# w' b * redisTemplate实例化
2 w3 {0 |4 U/ ^6 ?* p( k *1 ]! l; M* m8 j5 m7 C ^( x
* @return
9 n5 J) z4 Z% z) {9 @ */
, o) z% }, v/ @7 I3 \$ W @Bean
3 ]% S) j$ k2 ~ public RedisTemplate redisTemplateInit() {
6 y* ^9 F4 e: ]6 B1 ~3 j //设置序列化Key的实例化对象/ X' r3 Y6 v) i! v6 t5 J6 Q
redisTemplate.setKeySerializer(new StringRedisSerializer());
: s+ J4 a# J9 ? I6 N //设置序列化Value的实例化对象
! U& F3 k4 ?6 x$ `' z1 [ redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
. j2 `- |& `9 v" b: F- t return redisTemplate;3 }- k" ]4 e- H& y
}5 u1 ~7 k& i. q/ Q
5 L1 P. R5 u, c1 ?
}
" g9 N+ n$ y" r! r; B1 t 2.2:redisTemplate模板工具类5 L" d" R G9 K" G, F6 P3 {
: F9 K/ Q& o) [- x8 i
@Component
) B& U# {% [: N$ w public class RedisManager {# c+ w2 h6 R5 Y
7 o+ ^3 a1 s0 U" b# _3 i K
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);2 Y: y4 |! H* K" L, f2 x
i1 [0 k6 m* k* e% |0 n/ `$ R* O$ k+ W @Autowired) K m5 h) N9 M! u. Z" P5 A( A( P
private RedisTemplate redisTemplate;
& ]% F' I, W* B( Y; E / q3 P p3 b& l6 R$ u0 U# S( G
/**% {, H3 ^/ \% _9 ^% q
* 设置对象
5 {& T, s7 Q6 H *
( J G# B# {/ D8 h/ c' X * @param key key
8 n! P: n V8 ^7 I1 B * @param value value值8 a# W# u, J+ @, H; h8 l
* @param <T> 返回值泛型
6 ^5 y+ @# }8 l4 T/ n. ~3 J% B/ G * @return 正确的值:<T> 错误的值:null R% e& R/ h: W" P+ P/ S% `
*/( S) I/ Z/ Z5 p& `# j
@SuppressWarnings("unchecked")
! J1 ?. n$ \$ I9 R" L p public <T> ValueOperations<String, T> setObject(final String key, final T value) {
9 {# Z' |( i$ `) H$ \$ Z0 ? final ValueOperations<String, T> operation = redisTemplate.opsForValue();% i+ S+ U; C1 I+ |: r
operation.set(key, value);
2 y3 U8 b$ t$ f7 Q/ a# W4 w return operation;/ W, T+ o b/ }7 \( f
}3 B) I0 h8 @( \7 f7 C6 ]
( K' @0 s, x$ ]/ G7 I- ?% L: S /**6 `6 `" K$ t8 F4 v
* 设置对象及失效时间 (单位:秒)
' g0 B7 ?% G* c0 M *
' u! l& ]# i$ f) t3 ]+ s * @param key key
e( ?4 B8 I+ G * @param value value值
/ e7 S/ O+ I1 X. y) H- y% G7 A * @param <T> 返回值泛型
# L1 R7 r# Q) Y( G: t/ l$ o& _4 G * @param time 秒值
# l) y/ Z6 t9 m * @return 正确的值:<T> 错误的值:null
5 ~- N( ~6 j( ~9 } */
2 T3 z& j+ O9 D5 P; _3 e( F5 l2 x @SuppressWarnings("unchecked")
; d6 j0 n6 W0 I6 n4 S9 R3 a public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {' O9 b X8 Z+ t2 [; G4 Q
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
. Y6 H2 U0 K8 Z: C/ {2 ] operation.set(key, value, time, TimeUnit.SECONDS);
' {) W1 a4 _0 b return operation;
- _- h, z3 a7 u6 Q! m }
4 O3 ^2 J0 a/ O7 L2 F& c G. a 6 i* u8 ]- s' `5 `4 b) d4 ~
* F0 {1 U2 K7 X. Z1 w: z
/**8 L0 Q% _9 X2 Z8 ], l) P' }8 T
* 设置对象及失效时间(单位:毫秒)
; U' w/ N& ]: @4 k% [9 V *
( ?/ H$ T, D1 ?% V * @param key key1 M. B; {! q. v/ e
* @param value value值6 d; u8 j6 j: @7 x8 q
* @param <T> 返回值泛型
! s/ d) Z8 n9 `. `. T. P * @param time 秒值7 z T& Q3 j1 o# X
* @return 正确的值:<T> 错误的值:null5 O3 w$ Q. H' J, a
*/
# U+ M! u! X/ a @SuppressWarnings("unchecked"), W9 P: G5 j0 X9 n- C
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {! Q# y- T# c$ m5 w
final ValueOperations<String, T> operation = redisTemplate.opsForValue();
9 m& Y3 r! |- O9 f operation.set(key, value, time, TimeUnit.MILLISECONDS);$ `! h' A( M0 J2 v$ [
return operation;
2 X& r/ ?' j1 d# e }: C( A/ }6 ]1 q, u
: _( @5 ^/ P; ~+ j$ f9 }5 q /**, `: L( `; r* ?$ e9 q8 B, \% W$ g
* 获取对象
1 }9 t" Y9 u- y% I! L( l *
) ^7 u6 t3 k% d$ a$ s0 P1 D8 x * @param key 键. h" o$ X J4 Q7 X$ A$ Z
* @return 正确的值:Object值对象<br>2 ~9 s2 X5 }( S7 v% g- p" @* B
* 错误的值:null
6 R P+ R6 D3 a8 v0 _# k" i */
4 i+ ] k; R* Z @SuppressWarnings("unchecked")) X+ o, c( J) s6 a# k! x. K# ]
public Object getObject(final String key) {7 X+ O i+ c7 y9 a; ?
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
# @ K& d$ O. M. u8 q if (valueOperations == null || !redisTemplate.hasKey(key)) {, f5 F" n" n, E
return null;
9 l" O W% O/ T6 q- D }
9 D- Y1 H- I8 K5 V$ ?, u final Object object = valueOperations.get(key);
* f' L- i$ S! }8 p+ K) I4 { return object;
+ ^0 K5 M5 T6 u7 }6 c1 o; y }
5 _$ |& l$ L' d: ]: C 7 {+ j" {: [' X t n
/**% i2 r2 w! c$ k" X
* 从缓存中获取string值% d/ H0 B9 `& }; \& b/ z
*
% q3 C" r. _) M; o1 A* [7 h! @. h * @param key: X3 I' x/ O5 }9 b O! }! Y- J
* @return*/
$ R; H2 n3 P6 m, J7 Z& [ @SuppressWarnings("unchecked")
# r( D1 H+ W1 q public String getString(final String key) {2 F9 X1 M8 O1 p2 f' n8 a$ g; e; X0 ?
String value = "";3 }2 S2 V8 a$ `* |2 k, o. B( N
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
3 G4 Z3 V; [5 I% R' X' L if (valueOperations != null && redisTemplate.hasKey(key)) {
" F5 x! t: |$ C e/ F' ] final Object object = valueOperations.get(key);
4 _9 ~9 ]+ M, n$ Q5 \7 D9 |+ t: L2 o' h if (null != object) {9 X: n* u9 u+ f y
LOGGER.info("--getString--object not empty");' c% }4 s7 H% n4 g# T* F5 g- [
value = object.toString();
{, D G8 e: k# A- M } else {
1 L) U7 n& ~) e5 ? LOGGER.info("--getString--object empty");" q; C# K' `1 Q6 v& s3 ]3 l7 r
}
3 c4 f- G4 {7 Q! t: k3 Z$ ~* a }
1 L6 O1 L7 Z6 h v. D. P; ]7 y return value;
" d' C% M) n$ T" C9 | o }7 ^9 K4 ?+ V1 Y. d0 Z4 v9 U' ~
2.2:在redis中实现时间控制) c( F0 U! j6 \% `5 Y
: V& o( n5 ~5 I) @ 2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
) t9 N/ q" n* G- f 3 l R J& e' ?) P
import com.youjia.orders.redis.RedisManager;6 K' Q1 }4 j7 O
import org.junit.Test;2 l. F; A$ b8 O5 z f2 H
import org.springframework.beans.factory.annotation.Autowired;1 H( N, s% }9 h8 W4 J- A; f
3 {( S- Y: N5 X& J8 @: w8 M
import java.util.Objects;( [; `/ w+ @& _2 X$ O5 Z: U4 [& A
! W/ `) c+ {: I4 }* n" g% _
/**# [* D6 x8 ?2 |7 a6 ?
* @Auther: Yrion& L9 g( r: F5 l5 p
* @Date: 2019-01-11 23:36. u0 i& T" u$ p ]( n) \
*/
" {) F3 ^& _ D4 j7 Y3 r( _ : l1 `9 u2 d0 W2 ?* g
public class RedisTest extends OrderProviderApplicationTests {) w7 r1 ^8 R, U* q* u0 o
, b. K5 W$ [% G
@Autowired, j7 Y0 n9 c8 b) N
private RedisManager redisManager;
' J" l: T! |5 r3 U: t * k# p8 U# \& F( t5 n7 q
@Test
# m8 \2 ]" P) Q @6 k Z* g* `- ^ public void test() {6 f9 a. ?& f' o3 x' o) A3 ~4 ^8 e
controlTime("10000001", 10L);( ?" ~% k; d" Y2 d' W$ |) v
}
) _; z, o6 p' n4 }8 N1 m7 N! t ! k7 @- C& a6 e# o( G( y2 v1 c! J q% b
public void controlTime(String requestId, Long timeOut) {6 h; T+ C2 X, m/ Q6 q+ \
6 q2 F M& W+ {& B- ^- h7 r5 `
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {" k2 Y$ a0 v+ Q' J/ u4 H) k
return;
, a# `, [! D3 j3 i; S: T/ Z }
* B- b9 W9 g5 o* S k; _3 d //something code+ I; Q m: s6 U2 {. w
final String value = "value";
2 H$ }, d3 Q4 } J redisManager.setObject(requestId, value, timeOut);
* ^- c% l8 p: ~& a6 n& @6 N final long startTime = System.currentTimeMillis();* ]5 v$ [5 J- |* M2 h9 U6 P+ D/ G' ~
System.out.println("开始控制时间");4 m! R( [0 k' q' @3 M$ z" w
//start0 a0 D# s% l1 f) O" F( |) V( m
for (; ; ) {
! k& n% }, ~4 p, z( o1 h if (Objects.isNull(redisManager.getObject(requestId))) {. a& c; ~. a, Y( Q, Z
break;+ I8 Q$ _/ R& d; s& d/ y% q
}
2 ~: Q o2 `% ~8 W; }/ W% C }
$ s1 q! ~2 V# K0 I/ G- ]' R final long endTime = System.currentTimeMillis();% h+ w; j' Z. D9 m% d* I; b2 M
& S; {( G* j- G# c0 n/ @6 X" q
final long useTime = endTime - startTime;
1 [& q$ y; B9 R % M) U5 \9 e) n q' g! Z- ]* G
System.out.println("一共耗费时间:" + useTime);
1 ]* ?& d6 C* b/ F P- P }' ^0 x S" \2 }
}
/ L0 b% R0 Y! T" E5 X: [ outPut:
$ b$ O& Z( D. @( O3 {! r ; z$ f. Q% n, Y3 D0 ]- ?. T1 C N
开始控制时间; o4 r+ O" ^; i% f/ U
一共耗费时间:10042
9 e1 h1 L7 y2 S& p- }' }/ w8 N 三:总结3 G: ^ z2 I- \* K- W, Y1 K+ W
8 i/ H0 i" F$ F) h" o
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!9 i, v- _: H# Z' g+ b* s7 i+ u7 Y
————————————————' V( j) `( t6 V/ Y/ ~
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 p% H; E; J' m8 z' ]
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933259 i, f" P0 d8 c+ t
Q" R- d* s& f% m8 l* v6 x( _ 5 h% `/ E4 Y0 m, p
zan