数学建模社区-数学中国

标题: Java如何优雅的实现时间控制 [打印本页]

作者: 杨利霞    时间: 2020-5-3 16:03
标题: Java如何优雅的实现时间控制
  ~2 y- ^7 j9 b0 C
Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。, `5 n0 ^' r  ?/ m+ i! B

" Q0 b; B2 Y+ o; e一:时间控制的几种方案
- d2 O4 @( U; f/ D4 x
- I5 b3 }0 h" u: g3 a1.1: 从线程方面解决
: R# ?8 g! n) e5 `3 R) I" o: T3 V1 a; t
最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
' l2 j1 ]( a( _& H
1 [# y* j6 Y2 U0 a1.2:使用Timer& I# M2 ]$ w5 O% N  Q
8 U5 b5 r  h9 Y+ _
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:3 j/ p4 F' `) n+ A* ~- h
/ D* A: d) {) Q7 ]9 O4 u2 O
public class TimmerTest {5 @1 {( S6 q8 w
   /**
- z0 \, u" j5 g     * 测试方法' {2 E5 D0 Q$ O, b5 \; l
     */
# Q4 x% U- i. A4 a  E6 _    public void test() {9 o' M/ b" L5 A; I
        Timer timer = new Timer();: t& J0 T2 n8 _: U
        timer.schedule(new MyTask(), 800);
# P% _" {! Z# `# ~7 G8 g    }9 i$ [  k8 O- T0 _1 I) U4 z

+ S, ~7 W, \/ R" ^. S& ~    public class MyTask extends TimerTask {
* r5 r5 C. F: ?$ l5 {$ D4 A/ [
% w$ B% C( n% _3 w; r& {        /**
- k3 [& J9 j, T' ^5 Q' h0 y; f& i         * 运行方法
4 U4 P+ c1 F" v         */- \% S7 e- L3 z7 T$ C, n/ u
        @Override, a- R# U2 P: i' ]9 M7 ~7 D+ v
        public void run() {# z/ c) Y+ l/ A4 G8 h
            System.out.println("输出");
% P8 T( [0 x* Z6 p' G) ]: B        }
+ j) y+ g, Q" `    }
! P/ G, w, ?, t; t/ L; ~8 V}+ U( x& y. p% P4 S) u# C
这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。" a/ q% m4 y6 _- N5 C

& R) @2 L, g! @/ V; j  K1.3:redis延时
2 g8 ]2 p, Z. }9 G5 ]& ~! p/ l" k
) e) j# D( ^/ c- P2 X: o- [0 H在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:# H8 j$ c: {3 F2 G2 j* ?/ A

. u. Z: X- U0 z2 L6 z
2 @. b& G! j( e8 Z1 @% B9 n; T6 w6 k& P% y
通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
  I) g# n* Q- v1 F6 |* `" q' m; c4 o4 {8 T. m4 j9 e( Y" s8 Y
1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制0 Q6 Q/ |( S4 }: V; w

4 X+ ]0 t" w. T. D% s$ ~2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
$ r9 v, _! B- g4 h5 z) e$ e: C& I1 k2 h# p
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
5 M+ ?3 _' {/ m7 y3 f# ^- h1 K2 H% u* O) t( q+ [3 A6 a
二:redis" E# u. J! e8 t) |1 ~& h" |

! A8 |% V! H- G  O" p2.1:maven中引入redis
+ _3 d0 n2 {9 I9 |- K5 u1 y# _- A, V0 e
引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。2 d4 e6 d8 ?* s- ~8 b+ N

% J2 m4 b. J% l<dependency>
/ D  I1 A8 r$ t" v+ ^6 F5 J  ]  <groupId>org.springframework.boot</groupId>/ o) P! W( \  L' ?! I% \3 a
    <artifactId>spring-boot-starter-data-redis</artifactId>
% @; d' x6 `) D) d4 s# k      <exclusions>. G8 n. y1 M/ _
        <exclusion>
/ N5 w9 {; R0 g1 R          <groupId>io.lettuce</groupId>6 g4 L4 {6 @6 q) v& H0 Z2 Y7 m
          <artifactId>lettuce-core</artifactId>3 G' H& b6 x  H& \
        </exclusion>
2 ^$ B& f- ]# E) e  c- u$ X- `% s) J9 p      </exclusions>
* \; g4 D$ |. N! T8 b4 A</dependency>8 b5 A) X9 _' W6 l! \" Z3 v. ]
<dependency>
# |0 K# |7 x! L  C  <groupId>redis.clients</groupId>3 m" |- I9 l+ |0 s$ F+ Z
  <artifactId>jedis</artifactId>
7 K1 P! q# g+ X! e</dependency>
% t. K) }9 S  ~1 h* M; ~* G$ c& A3 G2.2: 在springboot中配置redis- J* A/ ?' ]! r# U( L

8 C( G% P; ~  F" V3 nimport org.springframework.beans.factory.annotation.Autowired;6 z8 n" ^) G! h
import org.springframework.context.annotation.Bean;
+ U4 r. u5 W7 c7 _  M# y0 eimport org.springframework.context.annotation.Configuration;
; d& A) B- B. |# i- t. |import org.springframework.data.redis.core.RedisTemplate;
& _% q) P* q4 Q+ y# _import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
3 G, a% t1 e- \8 d' U* |. limport org.springframework.data.redis.serializer.StringRedisSerializer;
; ~5 f$ S- o; O
* q- r6 F# \; x9 t@Configuration5 G4 z/ F1 r$ u6 u- j; V
public class RedisConfig {0 z3 T" ~" v- S8 b+ E5 S

7 l( f- O5 @: L4 V3 `    @Autowired
: I( j' y! Q4 |( w* c    private RedisTemplate redisTemplate;/ j; q$ @2 \" m7 d, T6 r- O# H
( e! l$ r  N% n! V
    /**
# c5 s" V( D% Q  l     * redisTemplate实例化, s7 e# m. v* y. ?
     *
7 Q7 u) s1 V2 `- u! W4 h9 ^     * @return
" R* {9 r1 {  r" }7 X! n     */
- M/ x) {# w" [& j8 u! W    @Bean
- `# f& ^" X+ D6 \& @) \% C    public RedisTemplate redisTemplateInit() {' n8 y( B% n1 l% [* `; x" P
        //设置序列化Key的实例化对象2 l  h* D" s0 S$ a5 s+ X
        redisTemplate.setKeySerializer(new StringRedisSerializer());: O6 c; D& D6 n  j* |- p* C
        //设置序列化Value的实例化对象% r6 `( m3 _  w/ g6 y* F
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());3 q, P8 T9 _9 Y0 U& X
        return redisTemplate;  |! i* O% F2 x0 d) U
    }
8 J, P& W) `' U% H- X; {* U9 b. z2 f6 X
}
4 S# E+ m1 ?' K2.2:redisTemplate模板工具类% o% ~3 S. S; ~2 [  d

0 p8 J$ m: v2 D0 t- D@Component) p  J% u4 ~/ e6 Y/ B2 b
public class RedisManager {
# Y( S. k" b+ A3 p* j2 ?  g2 X/ U  H6 @' S6 E6 V
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
. \5 e6 x9 _4 P8 B& F2 a- r& z/ Y% q5 d4 [1 e1 ?$ y4 i  Y. D
    @Autowired
% q+ T0 k9 G4 @# M7 t* ]    private RedisTemplate redisTemplate;7 k3 u0 Z/ C: u

; }  Q. v7 \; N" y: F( a    /**3 w5 o6 F. p* P, c  y& Y; s
     * 设置对象4 A% d* h( u. Y2 t, s
     *4 H$ _1 H# r1 Y1 f1 |/ `
     * @param key key
; Q0 j2 J. Y( D     * @param value value值
" b$ ~& j1 w6 I; w+ x. A4 d8 Y     * @param <T> 返回值泛型8 j8 D. w" \1 R
     * @return 正确的值:<T> 错误的值:null) R  n( V' m; }. C8 O+ O# n# _
     */
! \- ?0 `4 g) D+ b+ F    @SuppressWarnings("unchecked")3 Z1 j+ F( G* Q# W! s" [9 b
    public <T> ValueOperations<String, T> setObject(final String key, final T value) {& Q- _) w2 H) m. K
        final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 }( |# C/ o' S8 x5 x9 o. b, ?
        operation.set(key, value);
; L% |2 x: _( T; i        return operation;) E6 ?  K% i0 R! c2 ~& \$ c
    }
1 k# F6 J, o2 q5 v* O) i; i* b% ]* g8 t8 w2 T5 [6 a
    /**
" l6 Z' P, a8 z0 a. X     * 设置对象及失效时间 (单位:秒)
- i) A9 d: ?$ C/ [- S' o/ o2 {     *0 I( ]0 z# o- e
     * @param key key
% k; ?7 t! N% h     * @param value value值! a/ E- g: y0 A) d( i
     * @param <T> 返回值泛型
2 e6 E8 }6 h3 V( F) D* e/ d     * @param time 秒值7 {; Y4 W) M; ^+ X0 Z
     * @return 正确的值:<T> 错误的值:null
" @/ V# P. g/ x     */- R* ^" T! ?3 m* w' w! x7 a& s
    @SuppressWarnings("unchecked")
, Q* |* j; |: V. F" j; [    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
) D! J( I7 K, d! H        final ValueOperations<String, T> operation = redisTemplate.opsForValue();' g. O+ R+ }# Y* ^& n- Y
        operation.set(key, value, time, TimeUnit.SECONDS);1 h- r1 c4 Y  g4 [3 Z
        return operation;, v: H2 \5 d! q4 e& M. }
    }
8 K' g) F# @6 o: m, u3 }9 J/ r( N2 ~' {

$ B6 c6 {$ n, \$ F  J    /**/ W, D5 \; V# O1 C
     * 设置对象及失效时间(单位:毫秒)
+ R; w  l3 |3 z8 ~1 n1 V& |0 ]     *
# x0 }9 G& f( l" R3 \3 m     * @param key key4 w- N3 X8 P$ x  y
     * @param value value值
4 R& Q9 ^. |1 n" U; d2 F     * @param <T> 返回值泛型9 A. X/ J: V% g% x( g
     * @param time 秒值9 O& s( Z% _: K; r/ P  }
     * @return 正确的值:<T> 错误的值:null% }- Q0 G# m. }
     */2 v! [- Y! T! U* A" K& X5 I' Q
    @SuppressWarnings("unchecked")
! z2 ^' I. x# q0 a' V$ X, z: O    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
- t1 Z1 b& B+ \        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
9 }( s- ^1 \( ^3 Y; w& f4 f* z; ^        operation.set(key, value, time, TimeUnit.MILLISECONDS);
2 S! C8 W) a4 U" [' y* y7 m        return operation;
- a6 a0 a& W1 W. |; V    }3 s3 G9 ]9 D% |7 V" o. ~
$ t+ _( |9 P3 x- R: v0 C! \
    /**
1 G& s: _7 \5 L4 |# b; Y) l     * 获取对象
, [4 ?- x6 n' e9 f6 j     *
; k0 b1 _; o+ k* S1 N  ^' {     * @param key 键
% O. N: C: ^+ ?; L( `" g; v     * @return 正确的值:Object值对象<br>/ ~, p! i/ f( M+ T* \' g) ~
     * 错误的值:null5 l; Q# b9 E& d! |
     */# w2 v+ I& [, C8 A- z2 M
    @SuppressWarnings("unchecked")+ o6 J6 a, C- C/ |$ A
    public Object getObject(final String key) {+ }0 ?! R) I6 a& r( y& e, c
        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
% s* E! w1 g' ]2 H: |9 [        if (valueOperations == null || !redisTemplate.hasKey(key)) {- }9 b% E# _6 d4 A
            return null;
  m' K7 a; U; d. p- F% u, M& j* ?        }
! Z6 t" b7 h& B! f: a- }0 u        final Object object = valueOperations.get(key);8 s+ b# ~4 j) M9 R
        return object;) ]0 p; L" \; l- I( b6 n' i
    }
' Y9 b8 h3 K5 t& v* s8 ]3 E' k
5 |! b( }( z! T6 P& G7 `8 G    /**
3 z$ X  u- c# h  k1 A     * 从缓存中获取string值1 E  K4 U8 I1 y9 ~
     *
$ U  w5 G9 z* y  y& N     * @param key
" D+ V9 @" E, ?" D+ a( ]3 L8 S( t1 L     * @return*/
4 J: }1 U. v: r- d- x+ ]    @SuppressWarnings("unchecked")" M; k9 C5 A1 f
    public String getString(final String key) {
1 D0 F1 d" }8 i- B( g: ^        String value = "";
! D- ~9 w: ?! g+ S. s, M        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
* @! [2 p* d- W- f4 D        if (valueOperations != null && redisTemplate.hasKey(key)) {3 p# L/ ]+ E/ u2 _7 Z/ @: e. T
            final Object object = valueOperations.get(key);& P7 S, f5 }! h8 K9 A1 h( _( [
            if (null != object) {  q/ ?& K; P! M
                LOGGER.info("--getString--object not empty");
3 K' @+ f, Y  i$ D) v5 U- [                value = object.toString();# V: i, Z7 @8 w) Q
            } else {. ]1 I, e- V. D6 }8 \- c- b+ G- t
                LOGGER.info("--getString--object empty");
3 {- d0 T) d7 t1 u! o            }9 j$ L* j) u7 R) P" x% L+ m
        }
. |- z2 r' O3 v2 ~, Q        return value;7 I$ {% R0 i6 D4 G, K1 ~3 Q
    }
: M3 \4 o3 o. n2.2:在redis中实现时间控制( }: h* h" O9 L9 `
& h! L- R- }& P- p6 q
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
5 z, i$ l+ o& [. [. ^& [' y- g. w8 k9 F# |
import com.youjia.orders.redis.RedisManager;3 F& j9 i* G' Z' h3 y* V
import org.junit.Test;
- H3 D7 J1 s& o% t( Timport org.springframework.beans.factory.annotation.Autowired;' H4 `; `: v% z4 t
, c5 s) W1 X2 ^' m2 b0 }
import java.util.Objects;2 D6 s- F( g  W

2 ]7 y( v* |) o7 g2 E/**
+ `& w- W+ X, B! i * @Auther: Yrion
/ I( f8 K: J' b * @Date: 2019-01-11 23:36" {- ?( l9 q8 V
*/, a2 Q0 ~& Q4 B5 h

! |+ G* E- N* f' q* u( Tpublic class RedisTest extends OrderProviderApplicationTests {. \  }9 D# ?% d+ t, ~* y
/ `8 ^! F) B& x+ q1 h
    @Autowired
; R9 N  d# ^9 O+ d" A1 x    private RedisManager redisManager;
4 h# t) q6 P/ \8 K0 c! H1 ^/ O- n7 n* s0 E4 X
    @Test
$ t! [, t- s9 p! M% z    public void test() {# n% U3 T, T/ W: v5 A) F
        controlTime("10000001", 10L);) I* p* f( P3 D
    }+ P) H# `4 K8 Q; l3 K
; ]" W, L: S- {2 i3 j8 j3 W
    public void controlTime(String requestId, Long timeOut) {
7 {' M& F% m! @0 @* f  _5 B5 G! M  H, n. v! J
        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
) ^4 g0 Q) U0 b5 u* J& u+ [5 {- o+ q            return;
+ ~3 q6 N1 o0 ]0 l4 m        }+ }8 \$ e* J3 H& o. i" A
        //something code8 V1 W9 Z) w* K0 I( z) ?. d
        final String value = "value";$ j0 W1 Q3 F5 h" S
        redisManager.setObject(requestId, value, timeOut);
5 C) Y7 q7 M5 l  P" V% Z        final long startTime = System.currentTimeMillis();. T+ K2 |/ N5 B. o( [8 v4 Z2 w& u2 {$ V
        System.out.println("开始控制时间");5 a5 |# U7 c4 h5 s3 Z( e
        //start
% E* l; g+ G  Y- `        for (; ; ) {
, C- S6 I0 N: @8 R4 e; v9 a" \            if (Objects.isNull(redisManager.getObject(requestId))) {
# R$ t3 N3 z* y& y% z. f                break;
3 g. O% c) f; A& J            }# ^, k* k: U) g6 u( `; y
        }$ m8 C+ n8 F/ j, d# F
        final long endTime = System.currentTimeMillis();
, G8 q  ]3 v6 z# U3 p+ |- \6 s/ F1 l1 ^- P: H+ e- |
        final long useTime = endTime - startTime;: O0 R+ @- J! F6 q& V$ L

' q2 Z; N# d" W7 R2 C5 A) _0 M        System.out.println("一共耗费时间:" + useTime);
: |% G: I* E& A4 l    }0 f* B3 r; |/ H, q* s
}5 ~5 ]( h+ {  [) D: m
outPut:
5 r1 U! k2 K9 Z7 [" i5 r# Q* P( s0 ]: n9 I9 ?
开始控制时间8 L) O5 J1 q: i: x
一共耗费时间:10042
3 ]% `/ p$ a4 e4 n3 m( O三:总结7 m* u8 V% D" x9 n

9 `. |7 V$ [- ~) i& A本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
; J( ]2 p- [' ?3 C8 H————————————————: v6 d. r, e( C. ?; U
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 C$ S0 j+ v1 E0 B5 X原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
% O" n' q' r2 o) l7 t1 F/ f' D
" W5 E% R: W/ A- S+ `
) @0 U1 h9 K: @5 G" z4 p8 V
作者: 1661888967    时间: 2020-5-6 09:57
学习一下,感谢分享
" o5 Y8 Y  u6 P9 c




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5