- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 563403 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174244
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
1 S* j" I3 o. q8 V ?
Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。1 g4 u, Z* y6 t& r- ?
4 E$ G" h) g: x; E$ N6 f; t' j一:时间控制的几种方案
/ I. n- M; Z' n, w* R
0 k+ d& J( \! Y2 e/ ^% k0 |1 }1.1: 从线程方面解决
& d1 Q- ]( r8 O4 ]+ w/ g' I
2 V/ j, Z/ d$ G! j) m) Q最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
& g! i2 ?2 ~* g& ^6 X# f( g7 s1 a( W' q
1.2:使用Timer
" ]( _/ a; p" X* a1 c0 D# ^0 u: k7 Z# ?# v2 N
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
) D. B/ Z, }: {$ ?' d9 I: x! W/ {$ `7 m% F
public class TimmerTest {' t6 \0 H6 D/ b# `2 |0 `/ y
/**
3 `% e8 x+ I# P& T+ B% ?2 I * 测试方法
3 R. l6 J- q8 ?9 V. Z- D3 c& X */* \1 h5 t- H5 a% u- s
public void test() {
& F# K$ U; n3 _( S Timer timer = new Timer();% m6 F) q: o* ^9 b: H
timer.schedule(new MyTask(), 800);
# }; o% h- [' d, y) [3 ^% o$ ~7 Y }
* y" d( p8 K+ R( l R/ E$ N! l# g
public class MyTask extends TimerTask {# ^1 X# h& a1 @% M
$ g# P- z x" S2 P3 K! g/ ^ C /**+ L* Z6 X1 ~+ k5 Z
* 运行方法
7 k# y0 A/ g: V- v2 X */$ }9 ]9 g3 ^" ^7 `' A |
@Override, `7 q/ c( e4 M s7 S7 e* T
public void run() {
$ @6 u3 T0 c, ?) T1 R System.out.println("输出");; y. V- b. i5 @8 O, n# o
}8 w; W/ `# v! Y0 J
}$ {5 d/ `4 I& v' V W2 P0 m9 i" T/ @
}6 Z3 C3 S8 ]. |- y5 I9 {
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。$ V ~3 `+ b* i! k0 ^; M# g
+ Z7 U% d+ z0 c; n4 [5 {
1.3:redis延时
6 `" L) I' i- v9 n# q: w( P, D6 U6 w; N3 Z/ i
在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
) q7 T' [3 [% P5 c. Y% I0 }- u+ {9 ^) J% k7 h; Z, U. y1 K
& Q5 p4 n. f$ i0 f/ Y, E2 ?7 }3 ^- ~' v# a4 E3 l! ]! W$ E$ o
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
1 ~9 Z6 P- o# s' G ?/ a0 M q+ K* R' _# J
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
' W8 p1 E$ d" F7 [! |
. N( U. \, [, s' c" A2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
! X% A# @/ X" ^( n7 T% x) z
0 {( B9 d8 |8 v6 V3:简单,真正的代码实现起来只有很少,下面会给出代码示范。; Z% N3 j% y+ F/ x0 c$ _) P
4 ~8 G. Y' h4 {
二:redis
; f7 F4 X7 _, r; l+ T
|0 H+ q8 z9 Y# C2.1:maven中引入redis) a% N9 G7 y( a. {( z j
# W, N4 n: B: w) m
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
% P) e; c( W/ e
! _8 Z. S+ z% }* y8 g# F<dependency>+ J6 x/ P0 B( ^2 b- v- M1 F/ _2 S4 P1 T
<groupId>org.springframework.boot</groupId>
3 r2 A! | v6 T! a1 E. d% }/ z% R <artifactId>spring-boot-starter-data-redis</artifactId>: k' x8 E$ l, { Z/ ^
<exclusions>! F# x' N3 Y* Y/ ]* v/ l
<exclusion>! h' Y& ?" S( Y+ ~* T7 {+ z
<groupId>io.lettuce</groupId>1 Q" d; V @. h2 }
<artifactId>lettuce-core</artifactId>* T4 [) U+ L" u6 a3 l& w0 y% C/ l- V
</exclusion>
+ l/ E" D/ f8 \) h9 { </exclusions>( R6 @" p9 O. G8 {" ?; A; S8 w
</dependency>6 p ?: e, {$ q% U+ Q
<dependency>
$ P, m+ N2 J; q8 c. L/ z2 N7 U <groupId>redis.clients</groupId>4 t7 p! r8 g4 B/ U7 S4 }, P
<artifactId>jedis</artifactId>
- i, @/ Z- c& ]& ?2 ?! J</dependency>7 T! \" g/ M3 d7 @/ b
2.2: 在springboot中配置redis
# j. N0 T: D* {. \% C
! k; N$ _% O6 z" o+ P9 r8 @import org.springframework.beans.factory.annotation.Autowired;1 v7 s" F9 W: ]( N$ Z
import org.springframework.context.annotation.Bean;
& a* i- \* U. w4 [4 Wimport org.springframework.context.annotation.Configuration;
/ ?$ i6 a3 |% m3 cimport org.springframework.data.redis.core.RedisTemplate;: v B& d$ X* b; T3 i
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; ?0 U% I" C3 y# B) Z
import org.springframework.data.redis.serializer.StringRedisSerializer;6 y6 y; [) u" @% Y6 r. b
$ i% a, R4 k5 p* P2 C" Q" F@Configuration
8 r5 E+ W: E- `% I' i( tpublic class RedisConfig {/ J! v, Z0 Q$ V% \: @
- C' N+ x' K* T# S0 ?$ n
@Autowired y' F9 N& H1 ]4 a, K4 l. h* W
private RedisTemplate redisTemplate;
. L. C+ ~3 E. H9 y
2 @2 I: F! i, P" B /**
( c; C+ N5 m9 v4 z * redisTemplate实例化
V' p7 _. _- ]$ B+ p! p" D- p *
4 y# K$ W7 O/ R! I * @return4 ?* Y. n0 m; g D
*/
& V/ S; i) |( I( N @Bean; J7 B0 \/ M( f' W
public RedisTemplate redisTemplateInit() {
2 W, Y% b; q- g( ]! ?( Q J ` //设置序列化Key的实例化对象; U4 t7 ^: N1 r" [4 ]. {+ H
redisTemplate.setKeySerializer(new StringRedisSerializer());2 X: p$ z1 s/ W$ L5 i+ H5 |; K3 h
//设置序列化Value的实例化对象; W4 T! C& l: K) a
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());' y% h! n1 H8 m( ^
return redisTemplate;
0 M& E0 o2 v5 @' \9 _& R! V8 Q }
; S4 F- z) t5 X& F6 o& l: h( w6 d9 R5 [8 R+ ]6 S2 ~
}
. V' |0 f3 |0 h; ~; N4 ]# ~3 k2.2:redisTemplate模板工具类
) b# S( Z0 K# \6 z4 D5 g. p4 O2 F7 ^3 h3 n8 _- D E& n0 y
@Component6 l+ d, M# i( R* j' ]
public class RedisManager {* N' f# V( a: T; @% T
& P- Y6 I9 U$ ]' Z" [ private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);; M& M% I4 j9 {
% z4 S; P% R+ F1 a1 D. {: ?: ^ N7 u8 v
@Autowired
) i& {; D( U7 W% d2 y1 R3 I, F6 ~ private RedisTemplate redisTemplate;
- W4 t6 _6 V8 [. ]. X( ^2 g; [5 H' g' G" Y
/**+ ~9 I) ^' L+ q1 I+ o# H
* 设置对象
+ y% M1 P2 q/ G, M *
+ q7 Z& Y! t% X( l9 B; Y% k' O# ? * @param key key
$ K0 D2 o! q7 `% Q; ]! I9 R * @param value value值6 C; e" e) p+ N9 Y% v0 m
* @param <T> 返回值泛型
5 @$ W6 V" ^- J/ A3 \% r; d2 w * @return 正确的值:<T> 错误的值:null
) u9 f# F' Z9 u9 P u */2 G x! w: H+ |
@SuppressWarnings("unchecked")3 R- K( ^5 W1 ?$ N* n$ D R6 T# A! R
public <T> ValueOperations<String, T> setObject(final String key, final T value) {
, ~; N1 q [: Y. ?: E! N# F final ValueOperations<String, T> operation = redisTemplate.opsForValue();
6 x+ D8 n7 @' y4 K) c& R1 k operation.set(key, value);
2 y; g! u# R1 x( @- b" R% ?3 Y6 \ return operation;
) ~. T$ p$ g& h) }! E* I2 L, V9 R9 U9 M1 ~ }( Q$ U# ]- p# {5 G4 H2 n! p* u
+ y6 E$ N8 f! c
/**7 @+ V C" I- L8 ]1 Z6 j* Y
* 设置对象及失效时间 (单位:秒); T) i8 r6 V8 h
*
, t4 C' h* [9 b7 `& b * @param key key( G7 H' u" ?4 X/ v1 l4 ~
* @param value value值. p( y* c! j, |2 C$ d$ Q6 _
* @param <T> 返回值泛型4 V; ] `9 T) u# A
* @param time 秒值- ]/ {2 ]* R& q" M
* @return 正确的值:<T> 错误的值:null
8 w- u# j# N( k9 b; @. @- c */% b% P5 L: M5 c" S5 b* O
@SuppressWarnings("unchecked")/ z) R; f4 M: A# Q8 b
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
! `9 i! {3 |% V, j U B! L final ValueOperations<String, T> operation = redisTemplate.opsForValue();
/ o C1 [5 H$ x" \2 R operation.set(key, value, time, TimeUnit.SECONDS);+ H& p4 w. a8 C& d8 l; A
return operation;
; ~9 V( f& c: L* V7 Q6 H }
$ k0 ^- q8 U, X/ E2 N: W& @2 H
6 j d4 @: d$ f c$ s
) Y9 h4 H" u* [/ q8 }$ ]3 o' s1 j8 q /**8 e0 {: j, ~1 V! r1 J4 ^
* 设置对象及失效时间(单位:毫秒)
S9 A* j: K; D8 y5 M *
( F0 z% y4 s* S3 k" } * @param key key
; s1 \: A8 \2 T) f4 r * @param value value值
5 \5 C: J3 Y5 |9 |8 ~ * @param <T> 返回值泛型
6 S( O% u9 c1 U1 ^3 q3 q * @param time 秒值
2 j8 R( }3 W4 |3 Q * @return 正确的值:<T> 错误的值:null
9 s t4 B- ^1 Y */$ v3 [: L j+ c3 S9 F; d7 m
@SuppressWarnings("unchecked")9 L' W( f* c% X! `9 @3 Q( m
public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
( b) f8 X! J/ J8 x final ValueOperations<String, T> operation = redisTemplate.opsForValue();
( M9 _* x! g# L h; l/ [0 ]. h; g/ C operation.set(key, value, time, TimeUnit.MILLISECONDS);
6 N: l5 i4 g! S4 r! O0 v return operation;
' {8 N1 E9 A6 i1 N. ` }
. c9 q, s. o5 B( V1 S x* X4 _8 t4 b& M: W
/**
) p/ I4 m" q3 _- S! K * 获取对象
) W c: ]* F; a, q. Z *( Y! {, H! k- c+ {8 r
* @param key 键4 l4 ?2 z8 d0 C! g. `
* @return 正确的值:Object值对象<br>3 R& g# T2 J9 i* u9 c3 c, d" |
* 错误的值:null. z1 X# ~% N% h
*/! } ` h. L2 [3 o) u5 |5 n
@SuppressWarnings("unchecked")$ T; W% b" Q/ m
public Object getObject(final String key) {
x q& \& g5 H) S! J+ B$ P- c3 u$ V final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
# W9 u% V$ Z6 J if (valueOperations == null || !redisTemplate.hasKey(key)) {
% i7 }2 y( v' e2 P1 y return null;% X( h8 l" V7 J* J p
}; Z- z5 o5 u! x( K" b
final Object object = valueOperations.get(key);
0 B* D6 }. t# O8 g6 N+ G _ return object;3 K# X4 X8 j2 ^4 Z
}6 N* q' F& {$ H( x d0 v _1 h# x' }
5 s& ^9 t4 U! K( t& {( A /**5 Z- y7 I) T2 w! b# G+ @( x
* 从缓存中获取string值: p4 y. x, O5 B8 L+ u" [$ m: i1 Y* e
*0 ]0 I8 q. L- a2 z: q- x
* @param key7 X8 _( T% D' \" A! b
* @return*/( x: H8 ^1 L. b- @
@SuppressWarnings("unchecked")# n K6 Q8 y: J2 f8 }
public String getString(final String key) {9 {% a( N$ a1 ~, m' \% z
String value = "";1 m3 s/ E0 o' g! L9 n
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
$ U/ i* x& Q) g5 J if (valueOperations != null && redisTemplate.hasKey(key)) {
" s/ @# m1 z5 a* \ @ final Object object = valueOperations.get(key);
% M, g+ L ?9 M9 c if (null != object) {/ |! m: e: V: B; w4 P7 e9 M* `
LOGGER.info("--getString--object not empty");
% u! z3 w: J2 B" Q) a value = object.toString();* E' j8 y- j, l+ Q4 L( h+ v7 y
} else {
9 v9 R5 |% A+ H# a+ X LOGGER.info("--getString--object empty");
1 z* S5 S- P: c s7 T0 _ }
5 m. |" ]' `7 l- G/ Y+ k1 g }2 A1 M+ s/ e3 Y ?3 `. Q% e+ Q
return value;
* f2 x4 \; N5 D2 d3 S; i, s: B9 k }
# M# P+ I5 l- V2.2:在redis中实现时间控制* @, ?$ ~: U- Y |
) O( Z: f+ a. v* f |2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
8 B: ?& w! d/ p/ U
3 u# c" w7 W, w3 simport com.youjia.orders.redis.RedisManager;9 v0 F0 B5 N9 |! ^$ G/ L3 {) B4 T M
import org.junit.Test;
N, L0 {5 h+ [# simport org.springframework.beans.factory.annotation.Autowired;5 `" F e" M- v Q. b' |+ I2 y$ a
+ S9 p& H$ e; o: @/ n% Wimport java.util.Objects;& T+ \4 `( K9 y+ g z
9 v% C7 H+ A' `: n2 Q5 G0 b4 j% [/**
: x& R6 c- j" C0 [# A9 \8 O6 r * @Auther: Yrion0 j) z9 ?/ v- d9 ^) s
* @Date: 2019-01-11 23:36+ H! K& W4 N# j M* Z e
*/
/ d8 o4 K) \/ E9 R3 w2 J8 v/ c9 F `( G
public class RedisTest extends OrderProviderApplicationTests {& ^1 `$ }. u% P6 Z V
* d3 X% v1 s# J5 w& U
@Autowired# S% M& l# w; }3 H" L
private RedisManager redisManager;& U6 e! X8 \3 V+ J2 ^
* b5 k+ E( p& C$ a s
@Test
# }$ C4 E2 o/ A8 v& Z" ^ public void test() {
0 E. {% Z4 ~: J controlTime("10000001", 10L);
& g9 Z+ ]( P0 m) T" A- z- u }4 y, }, v4 I) u! p
8 ?( n; I( ~% k, ^7 U' M: ~
public void controlTime(String requestId, Long timeOut) {
/ E' ?, w+ ]/ `. Z. t$ r( {" l' b) s! Z c1 l5 ?) o" N9 S' |
if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
& u& b+ k* X$ H4 ]9 y return;. I$ g! M# R; N. A, l
}
; W0 p9 r) h( G0 b //something code
% W4 U/ \3 |7 W% ]# a1 z6 E final String value = "value";. D6 H9 Q% o) [2 A7 z: a3 B5 `
redisManager.setObject(requestId, value, timeOut);
# z' x1 [6 q$ a8 ^# |8 x final long startTime = System.currentTimeMillis();
7 ~0 F# p9 j; r. U System.out.println("开始控制时间");# b" b4 h$ Z* y0 ^
//start
. Y9 ^8 R1 V- P for (; ; ) {7 p6 l) p4 _9 N. |& j7 r9 ?9 t
if (Objects.isNull(redisManager.getObject(requestId))) {4 ^( P# e! E0 p, h; i( H# @9 [9 U
break;
% T( x6 V+ m% S0 X7 \/ i. b% ~% [ }5 C3 m9 e/ t. J0 L$ f/ Z5 N& O
}
3 A+ ]2 J4 n$ E8 x7 P final long endTime = System.currentTimeMillis();+ ~* W& \/ X1 D
, ]! i" y1 I; M' [
final long useTime = endTime - startTime;
. l5 b3 K( }2 E! v3 J# E8 k
. Z0 |1 @" Z0 r1 H( v; E# T' D System.out.println("一共耗费时间:" + useTime);/ } a6 r1 }, Q. g
}
5 K" y' x3 ]7 k$ l5 i. P}
9 L( _" K8 Q( z( z6 F9 ]outPut:
% C# U% p; x# L0 H) A
1 ^& q0 r" ]5 ?' u) ]开始控制时间
$ p1 ~7 V7 M7 G一共耗费时间:10042' g1 ]. a* b; k& d
三:总结
' n7 }+ |- r4 g
0 m1 V5 b4 q7 m本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!( Z* n+ c2 g: q
————————————————; C6 n" d7 g9 b2 a5 {3 j
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
- r) D4 f( n+ X" ?3 C$ |6 G原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325* p# |+ e' {& E
, M+ q1 I4 K) L; b U0 J
' m/ @- }- c' a! U3 y |
zan
|