- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 564715 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174637
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
/ x7 f& H% S; x( g5 F2 PJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
% s/ R# ?) T+ k/ D
7 e* c8 j a: F) ]2 z一:时间控制的几种方案
# |4 m; f; E% U& W/ a! \! O/ A" ]0 R7 |8 M# y6 ?
1.1: 从线程方面解决
9 T8 ^7 s+ D) x6 Q
$ _% i1 f' q9 o最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。4 \/ {2 D0 ^7 k. J! R
5 U4 i2 L' S$ R1.2:使用Timer
$ d1 ~( t' A5 C: p3 u" Z4 ^9 i) }- @4 M4 N0 X ^- \- A
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:7 M. f' J, k" ^5 A# Z6 N
5 z' ~2 \1 g* i! t2 p' t
public class TimmerTest {. B l: M; g, f, U& u
/**3 L- |4 }0 B9 _1 m7 b- K
* 测试方法3 C2 H) y+ L, \" k9 Y P
*/
" Q# s$ ]. f& S/ Q public void test() {
: k, ^+ p+ j( `1 u Timer timer = new Timer();
' T( E6 ]$ D$ U/ e0 N% a0 C. `( N timer.schedule(new MyTask(), 800);1 g3 i& |4 }; S+ o
}6 i- r8 l6 d! p/ }9 g- V- X! S! P
- n# G. U6 {: ?# y
public class MyTask extends TimerTask {
# a3 f. C4 ?' _9 v" S# N
8 E2 L) ]0 s( c6 z; C+ C! g /**
7 C: |( j5 B2 m# \) o- K. r * 运行方法! w) l2 Z4 B8 r
*/3 U/ Y5 { P9 C& v
@Override
- D4 h# \) t3 E7 Y$ U- ] public void run() {
; o# d9 p( H. h/ F5 F) ~# Z System.out.println("输出");5 Z! ?( X. \+ f! v* @
}' [/ x5 X/ J6 M6 t G" m6 F: P
}4 g; D. Z! Z$ U( A$ O
}
5 M% i& ~! m/ H' }9 |这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。9 Q2 `# M* |2 {: i/ V, w. P
! I5 g/ {6 b: E# Z! b$ K& R7 b
1.3:redis延时
% ~* X& M& K3 h: U, g, l, U2 T
1 I# L N# R# v) K; r t, L在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:+ F% F4 _3 J0 m, y8 V0 J
0 I2 w$ K7 z/ {5 ?; ^ s
- l7 E. J+ {5 z* D3 ?, t" c5 h4 Z7 b) l
& f/ l- D m2 s+ V/ P
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:4 j) \1 ~( c4 y0 Z$ i
$ V% g* M# |; j2 f
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
8 j* n& y& n5 g! }6 Z) k
7 S6 |5 T3 t) ?& |( W4 [5 q2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
; V, j# L/ j1 y' W4 g
* F! z; N: R. B x# r% Z0 _4 u: r3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
j Z; A% p: H; H3 s1 [% J3 P' Z |+ @. X% N* x$ I ]
二:redis7 i3 W* d/ u0 d% W. a: {
1 k% x# Q' J1 W p7 M$ H1 Q2.1:maven中引入redis
1 Q$ L0 y* c- Z: E! K0 r
( R# g2 x( W' u0 |& I- k, E* y引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
) k. f, D$ D2 w5 @+ J0 Q3 k+ k8 k' Q1 `$ B3 C
<dependency> M! B6 z" p* V5 U$ F
<groupId>org.springframework.boot</groupId>8 f4 b- x* F6 F
<artifactId>spring-boot-starter-data-redis</artifactId>
" s" T; W, ], G9 i8 q' o4 h& o! Q <exclusions>
. m8 I. [% C' M) s; ~7 Z <exclusion>: K' y+ z' q4 }, c# z
<groupId>io.lettuce</groupId>6 r, D: s" `4 {' W0 T
<artifactId>lettuce-core</artifactId>
( |. Z/ ^ o7 D# V$ f8 z; l/ Y </exclusion># a& q/ i( t) {$ V
</exclusions>1 D. ~" ` {. E, ?) u* b( r& A
</dependency>7 Q3 g- Z* G! W
<dependency>3 ^7 r! p' C- j: A3 f
<groupId>redis.clients</groupId>$ u% H* U9 y( [! s6 W
<artifactId>jedis</artifactId>- P1 q+ ^ U4 d4 N1 B3 y" q% c
</dependency>- J& X3 c+ i' ]1 ~6 r& s
2.2: 在springboot中配置redis
! I' a, O+ ~ }! L: ?/ l
# b, f& k0 b7 I) k& Ximport org.springframework.beans.factory.annotation.Autowired;
' w0 b t& l& K% b( c0 P2 timport org.springframework.context.annotation.Bean;
% A( ~3 R2 O2 e& I9 N! Fimport org.springframework.context.annotation.Configuration;* a) W o* E/ B6 G7 y# f
import org.springframework.data.redis.core.RedisTemplate;/ R' s5 S* h' C' [7 n
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;$ _2 V5 M l2 w3 u) b
import org.springframework.data.redis.serializer.StringRedisSerializer;7 w3 s; T! T& i. n4 L
( Y# Y; B$ [6 v, j% ^( v/ N@Configuration
" G) p% U2 d |" c Qpublic class RedisConfig {
+ s/ Q' j) L* h( Z, }" d0 c+ S {; e. v! @1 c1 k. p
@Autowired2 o/ D) b" o. d$ v7 @& @0 Y
private RedisTemplate redisTemplate;
" b" G$ Z4 a- K$ c* B+ t# V7 j/ a% r- \+ K) v- w9 Z& {
/**
# c# {" G9 n: m * redisTemplate实例化
& Q9 K! I7 V |* F- G *
0 W, Q& J9 f8 h1 t) i! R1 R * @return) }3 X c: |" f5 Y4 u2 u
*/
1 _; j; j* ^9 O- r8 G$ c' j) ` @Bean9 u& Z8 ]4 U b' W# M8 H* q9 ]
public RedisTemplate redisTemplateInit() {: ^" R+ F5 r. X1 m: G3 z- m
//设置序列化Key的实例化对象
* ?2 y) K/ F# p8 @: [' E7 U redisTemplate.setKeySerializer(new StringRedisSerializer());
+ _" o* a' Y& Q: ^3 i //设置序列化Value的实例化对象
8 l1 \% {, c! | x: ?/ A) s redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());: r5 ~8 J( v2 k0 e' R
return redisTemplate;) a( {" Y$ I3 y5 M9 q
}
) {& P; ?7 |6 p2 t8 T- `9 U' q5 ^; _ V4 t
}
7 B3 d, h) e/ a, \9 J2.2:redisTemplate模板工具类
& o* `# @# N, W$ R4 _& ]
$ n, \ a0 ]1 j2 }$ B( |& Y6 x@Component( ^+ j; o6 [4 _: x/ _( M
public class RedisManager { r f: d( q/ M O$ m4 c
* W9 N; r5 t6 d x1 W
private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);. V4 l$ e X4 R
6 D$ f, l( Y4 q4 u9 V8 T9 U. y @Autowired
* z3 ~: `$ ]' @/ B5 r0 m% |5 r: ? private RedisTemplate redisTemplate;
; G' Y4 E, l0 ]/ s1 V! l8 V {6 M* f6 [' m0 H4 q
/**6 o+ T; P" j7 a8 j0 B+ I9 ?
* 设置对象3 ~9 _* P" ^2 S+ C# ` I3 Q4 R2 G1 n
*
3 r: a% J6 c4 H% t * @param key key
# d7 G* }, d& _$ a2 T* ~ * @param value value值, n3 `. u8 j' j( R9 C) Q7 B8 J& q
* @param <T> 返回值泛型, l) A$ G0 W: k, y9 D- V' Q
* @return 正确的值:<T> 错误的值:null
) H p: |- V1 b */
1 Y5 z- Q* p: m: v o @SuppressWarnings("unchecked")
0 T- A# r5 @% l& w$ b X. ^1 b public <T> ValueOperations<String, T> setObject(final String key, final T value) {
6 ] A$ T |( E4 M$ e `4 p3 y9 x4 ^ final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 p, L& g% O' m7 ?; r
operation.set(key, value);
; x6 u% J5 H0 l' i return operation;
h* x8 W, B* d, y }* f4 J8 e; w( T: H
" \: I7 Y! `3 ^9 b' p- J8 S /**
8 U1 k8 `- r# o7 J( V * 设置对象及失效时间 (单位:秒)
: j) X% z) d; S *
& Y) b% r7 u# C# j3 B- n2 r4 N3 } * @param key key
: E s2 I0 g- I5 ^/ W" K: ` * @param value value值
' y3 Z2 I1 S. S3 W% R" F$ N * @param <T> 返回值泛型: p, w* F, o; ?
* @param time 秒值* |, Y" M! R6 _, n4 G& W4 i+ Y' T0 x
* @return 正确的值:<T> 错误的值:null+ \7 j2 V: {( N ~# k" p2 A
*/' r2 Q4 W. H* f9 ?
@SuppressWarnings("unchecked")7 c, l+ k; R' U6 B4 i
public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
- b" a- _. F. S* A final ValueOperations<String, T> operation = redisTemplate.opsForValue();& b1 v" O, k# ^+ m6 ?
operation.set(key, value, time, TimeUnit.SECONDS);
1 ~3 g' f) P# @& Y( [3 S( o return operation;/ i, W% S9 W+ w4 T
}$ @( ~( Q: c9 N: E7 L
2 q" P2 c3 M3 a5 T. l' r/ n
& S: d$ h/ o8 w2 q( p7 Z4 e# m /**; _8 _. L `/ K6 U7 z$ n. l
* 设置对象及失效时间(单位:毫秒)
( Z! j$ N5 X- c9 i& N2 D *9 P# V; s/ Y* A" F) \4 e! i. E
* @param key key
0 y6 M1 _* Y$ `7 }& d * @param value value值 m5 R5 w) q: G) Y2 P
* @param <T> 返回值泛型6 s2 S+ w9 ^, F0 H! o
* @param time 秒值/ U3 h5 }8 M9 k$ \
* @return 正确的值:<T> 错误的值:null
) H7 u5 J) T1 r( z4 R */7 B- Q1 j% }% Q) j4 g; Q7 a& E
@SuppressWarnings("unchecked")
; `: b) E" I/ ~/ k9 \# j! h& P public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
( k+ L) t" A/ ^ final ValueOperations<String, T> operation = redisTemplate.opsForValue();
* j4 X* W! M. V8 O6 {; h7 u operation.set(key, value, time, TimeUnit.MILLISECONDS);* Z" b3 q- [3 m0 E- ^
return operation;) M3 G3 L& j+ E5 @
}
& E' }: `( G3 `) R. f' O e- p! ^, `$ X# Z0 S9 [
/**, k' |+ U4 x5 ?; E+ l% ~
* 获取对象
; T! L3 u; E* N *
6 G! T& k. @+ U* O# b. f$ i7 ] * @param key 键: L6 N" {- K( E. p' A8 K+ T! O9 p
* @return 正确的值:Object值对象<br>$ I k. `" {/ m/ C- k
* 错误的值:null: d% R' w, h+ d2 S; D; B7 ~
*/
: h' H$ ~! {3 ~3 c6 p1 u5 P. n @SuppressWarnings("unchecked"): ?0 U" R N* ^4 N! g6 b
public Object getObject(final String key) {" ]2 L1 }, V' ?2 ], \& D
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
# D# p+ \/ q9 o' S6 r+ e2 \1 ~) I if (valueOperations == null || !redisTemplate.hasKey(key)) {
) g3 o' Y7 K" H/ z8 Y return null;+ y. p5 V( W% w: }
}
( s( s0 d* |: C* n7 ` final Object object = valueOperations.get(key);% n9 [$ P, }1 R
return object;
/ [9 i0 P8 U8 \0 c Y }
2 E; E6 `6 t: @; ~
$ A: J7 M' ^& w" \6 p5 f9 z; j /**
5 X# P: S2 X6 b/ D. V( [ * 从缓存中获取string值, ^2 y! |' v- F1 ?$ w: C
*
( B9 V( U% D3 U0 | I9 _9 A * @param key. w/ Y3 o" C: u/ E) o" V
* @return*/' g% U f& e0 E1 C7 |, U! z; u+ W$ j
@SuppressWarnings("unchecked")* }- V- [7 G4 c. I4 Q) \
public String getString(final String key) {
5 e$ @; z# F6 ~, ]% t' | w String value = "";6 z: ~$ W2 H% {: g" I, Y# O' I5 A
final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();, x) k# p* T U' _
if (valueOperations != null && redisTemplate.hasKey(key)) {
, h- |9 f' D2 }# i final Object object = valueOperations.get(key);
. ^3 Z7 g+ R) a! o* n( Q& } if (null != object) {; M2 |. c+ I" Y e6 w
LOGGER.info("--getString--object not empty");
& e; t1 N; E1 v8 T9 _+ x6 I7 Y value = object.toString();! h; u/ N( t' s/ e" E5 z4 c( v
} else {% [! g3 s% `2 e
LOGGER.info("--getString--object empty"); W- V/ J; O# O! y r _
}
8 ]& o7 Y4 u4 O0 F3 z }3 y9 I" T, V2 q8 h/ d* ^
return value;
+ U" ^3 f5 J# Q }
w" S3 y6 s4 [2 ~4 J4 p2.2:在redis中实现时间控制) c. M& J# z6 Y8 u( O3 N1 N8 r* }/ E" I/ i
# m3 p- {" t' i, l; p. `
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
. V! K2 l7 ^8 _5 \( q
& W( M: B% ~! Y# T" cimport com.youjia.orders.redis.RedisManager;
1 n9 o6 `. A7 n( _import org.junit.Test;, W/ i, B* A- l: G; t$ d( `
import org.springframework.beans.factory.annotation.Autowired;! I* q. m9 |3 g9 P( T/ H. ?. I! T, ^3 o
) H. C# W" j- V/ [5 H4 f Mimport java.util.Objects;
h. q6 M/ P ^# b
) S6 x Z! c8 x6 F$ f0 m/**
: b9 u. w+ @8 d- x * @Auther: Yrion6 {( f) s) C3 N( l; f z
* @Date: 2019-01-11 23:367 N+ l4 f {2 Q* m6 r* v( \( D; q" x
*/7 y* i ?# X% V/ l6 r; Y( a& G5 I
! ~. ]4 T- g4 `0 P, W
public class RedisTest extends OrderProviderApplicationTests {: Y" | l) d/ T8 I( B
! s5 L/ Y- A' b) Y" @1 c0 W1 S
@Autowired5 P2 ]# ^, V V0 w8 j+ k
private RedisManager redisManager;
! P" B1 u# X$ _% S$ O. Q
N; O# v! m4 q Q! y( Y @Test! A5 ]" ?& o6 M- j3 z% H, e
public void test() {$ x! L0 g! j, E8 C% k; [
controlTime("10000001", 10L);
4 L% G9 u/ q$ B" V& T }
( {0 s. a$ Z1 {! `3 U# ?" @' Y+ F8 k# ~
public void controlTime(String requestId, Long timeOut) {: G- Q8 E7 B( }7 _
5 B; N: Y0 t5 c" V# f. D& u if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
5 l5 p- Z; k+ e$ P1 o return;& @" s0 R$ u2 P9 q
}0 V) K4 q' y; [5 q/ N/ t
//something code2 Z/ i8 G& [1 g9 D% S, }1 C
final String value = "value";8 \5 A$ k3 S K; P9 s0 ?( W
redisManager.setObject(requestId, value, timeOut);
. s6 I; j. w o3 D final long startTime = System.currentTimeMillis();
5 {; ]' r* x t$ L7 [& _ System.out.println("开始控制时间");+ Y( r+ `) f3 j* w7 U# H W! [9 p8 l
//start. T6 U( m' X3 }4 A, n1 Q, y& F
for (; ; ) {
( {! A o9 |/ a1 a( p if (Objects.isNull(redisManager.getObject(requestId))) {
3 w, i% q o0 ^# ^$ k; T% o break;
6 v' ?" E( P) z0 Q$ p3 q }. X7 B: O) w/ T3 H$ w
}
/ A; S( ]' l: a! E: H final long endTime = System.currentTimeMillis();
* w- G" N6 Z( f0 T1 w
0 M8 w8 W T( Q* M: P; H final long useTime = endTime - startTime;: G: |/ U) o7 V- {: G
$ U! R: M: I: x
System.out.println("一共耗费时间:" + useTime);0 l1 E$ |2 q* d# }; @0 a$ J3 }. k
}, N( z: [3 y8 Q; {6 ?( t0 w
}9 s% B2 E" Y- @1 G {2 H9 |
outPut:
/ Y5 i4 o% l; c: X8 ?3 W. K
+ W' g. o% h* Q/ M0 ^开始控制时间+ Y, M( I- L5 _ J Y) A/ ]9 Q
一共耗费时间:10042
8 l5 G* g7 V [& z; ]8 s2 r% T. O三:总结) r) @0 B6 U6 q% V0 V2 m
" q, M$ t L5 s1 a
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
- ^1 ~1 k6 @6 n, t4 S% ? [- J+ N————————————————
% }" w$ a. g0 D. m2 D; f5 e版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" q% h8 x4 s, _+ }1 M. e% E' Y
原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
% {; |# N1 n3 c0 R+ K$ |# S
/ n @5 Y6 r4 ?6 n; N' w8 X: p9 G+ \
|
zan
|