数学建模社区-数学中国

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

作者: 杨利霞    时间: 2020-5-3 16:03
标题: Java如何优雅的实现时间控制

" p. G( {  D4 U" L# x3 IJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
9 s; [5 e1 V7 F, o% m6 Y
3 s' I4 o, B# {5 ~一:时间控制的几种方案* Y9 L9 R5 i6 l* N
, [) i% [: Q2 V9 @6 \
1.1: 从线程方面解决& h: }$ X9 \9 Y6 H0 M

' D7 m4 M: V9 F0 A- C: ~; x最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。& A5 H( o( v1 P7 R3 P' w) D: b
' c' g2 g" P( F. ~
1.2:使用Timer
) v3 x& n4 f- o- x: d7 P% K3 Q! t$ _% L3 F/ C6 n% n
查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:  A7 m# l2 ~# @/ ?) V9 h

8 S" I$ E4 _1 @7 t; lpublic class TimmerTest {5 [: R3 d* [0 n$ U/ K9 G( w) _, a9 p
   /**
. L+ k7 D, K- b5 H% n     * 测试方法8 ^' c4 u8 }" A! \  ~3 g
     */0 T1 G* l1 u0 M( ~" {
    public void test() {9 b+ s' j: G; \; K1 l0 v/ U8 w
        Timer timer = new Timer();
0 d4 C; p$ ?% h5 D4 z) H        timer.schedule(new MyTask(), 800);
8 z9 H- {5 R! }& Q4 v    }
$ @9 v- r6 g$ y* N
1 h" v! A! C, F2 a+ h/ u6 H  }4 V    public class MyTask extends TimerTask {" i- E0 r: k+ G! V: ~: z0 \

( ^& `% f  h4 {+ f        /**1 ^. T# d, c2 f4 {$ s! l$ L
         * 运行方法& t( G7 p$ X9 M1 _/ g  Z
         */+ I) T. [) V+ k+ _' x1 g
        @Override0 {2 F' V2 |0 D
        public void run() {, `. t3 I7 H2 G9 R: Z4 B
            System.out.println("输出");3 L$ F# I* X8 ]) E1 D) e
        }& W2 K+ {- c! D* o& i
    }
& f: h# ~1 e- p. ~}
- a$ F% C. @0 {; {, [这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。) E( M7 |9 y9 |/ ]/ v6 v( W  u

  C9 S/ J$ X+ a  v) z. @( [* ^$ m1.3:redis延时
/ V6 R6 B- ~% i6 b
( Y* i/ A# T8 T" A在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
" r+ T+ Y  K' v/ m  x9 N2 T$ s0 c, ?7 w( P0 j* g. i) k" s/ [: s
1 |3 e0 L& h" W) V! e7 _

+ v- c4 o2 f& e# W6 [3 I. p通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:: p. @7 ~* o' O" x' T' r

5 I4 v1 h4 }; m* _' U0 d- A1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
4 Q8 m, |3 a4 d, I1 S9 S& J7 G  @
2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现# ]& ^. d/ f: Z
% W2 Z! n( o! X" p, b
3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
) c  L9 E: p& D! F6 Z1 K) C2 p, P2 ^2 l; h- i+ G1 T
二:redis
& O- ]# G1 X: ^" F% q
; B4 V) c+ I2 }& t/ w9 S2.1:maven中引入redis% a& B% T8 S( ^! Z& N8 i$ K+ p% S

$ y1 h# q& y1 W: p! Q3 W引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。) x! a, s1 x: C% O, C* [. Q2 q
$ J$ E' H6 ~. h  B% p/ \+ {% B6 T
<dependency>
  I8 ?6 e4 H5 q* Y  <groupId>org.springframework.boot</groupId>/ Y+ L! v& v, y8 o" o+ V1 Z: T
    <artifactId>spring-boot-starter-data-redis</artifactId>
  ]. [& R& g* ~      <exclusions>5 e3 q+ T( i8 X0 d4 K
        <exclusion>
4 `$ n8 M# B& D: I4 X4 r; b          <groupId>io.lettuce</groupId>5 K1 j/ K: E' U0 M) l+ w
          <artifactId>lettuce-core</artifactId>; M; |8 M3 h4 I+ d0 m
        </exclusion>* N% b# l, i9 X) h+ e1 j
      </exclusions>
" @: Q+ z, U+ ]% E</dependency>
1 S# z) G8 L) f1 r* w- e, H/ P<dependency>
5 s8 o8 A) M/ y# k1 K0 ~( N  <groupId>redis.clients</groupId>
0 v; G/ _# Q& i# `) y$ P  <artifactId>jedis</artifactId>
! @: J) \( y& B' i5 G. u: Q- B! V</dependency>
4 p% h4 V) y0 ?0 i5 S5 c5 ^2.2: 在springboot中配置redis
" T0 s& O0 d/ m3 \, ?+ g& ]) z/ G8 x8 i1 w; m& W  v+ _, N
import org.springframework.beans.factory.annotation.Autowired;% @1 f1 y- ]+ t& m0 _
import org.springframework.context.annotation.Bean;
5 M" K9 T- }" \% e% m- T* R) jimport org.springframework.context.annotation.Configuration;$ _5 `: W* U" ]
import org.springframework.data.redis.core.RedisTemplate;" n" @  r9 y$ d  o: {& c
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
( H: T( D- q) C8 N  J8 [import org.springframework.data.redis.serializer.StringRedisSerializer;5 D, q3 C8 S' \8 j
+ c  @* k# H4 _: X! F" p2 J8 a% \
@Configuration
6 n$ z( X+ m! Tpublic class RedisConfig {
( q7 O; m; X% t/ c3 F
3 x0 K. ]. Q) B! G5 W    @Autowired
( Z+ v( o: r% ^" x+ _5 `7 W    private RedisTemplate redisTemplate;
. U4 [: @" `$ W3 _, S! }) E0 v4 \1 s. J/ g
    /**
9 }0 A/ u' d* F, l4 N" H     * redisTemplate实例化
% }) p  I' V. u; [, K0 f     *
+ K! h3 W* S" d2 p     * @return6 L* d: g8 p% f' H/ w! n1 u8 O" }+ z
     */
$ s/ i* `) T1 W8 f# M    @Bean
7 N- v; Z8 B& M- A: K( V    public RedisTemplate redisTemplateInit() {  F3 \$ B/ q6 |9 e
        //设置序列化Key的实例化对象# _; G" N+ J! B" j( d2 _7 M" c
        redisTemplate.setKeySerializer(new StringRedisSerializer());
: Y0 l/ @" j3 m7 r        //设置序列化Value的实例化对象
( }5 {' p2 o. k        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());0 Z' g$ P0 F: q# O& {5 |
        return redisTemplate;5 s  e. U: t7 K) [& G4 x
    }6 }3 K* ]) K4 ?& O, r
8 j0 a" f9 A! a0 b3 B1 Y
}
1 F0 v' N, }4 }2.2:redisTemplate模板工具类% L* {8 x& Q8 O) v( f5 F
9 g7 x( p0 p# `$ G+ \
@Component* l) H! D8 l$ @
public class RedisManager {
% V3 P4 G( o# }/ t5 a
% M' h4 X0 s1 Q4 t* \    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
$ Z0 H& F6 m: S- K7 C/ x
$ @1 A  n4 b3 B- r7 _    @Autowired  S( _6 Q" n, O# G6 Q
    private RedisTemplate redisTemplate;1 d: G) T8 |+ i& F8 s6 i

' B. x( C# E, F/ K. j    /**) j4 ]8 y+ t8 ?
     * 设置对象$ ~9 Z" L) A' t( r8 e0 V
     *
) Z# X" k+ z) V2 S& |  y( m' a     * @param key key
3 Y3 T/ M0 ?  m7 `8 _     * @param value value值
- M0 D1 q, Y1 Q5 P2 D* m     * @param <T> 返回值泛型1 `3 Y8 v) V& }/ F- y& Y
     * @return 正确的值:<T> 错误的值:null" {  t: |& l1 Y4 ^$ \
     */" I( K4 Z, l# h" r) t
    @SuppressWarnings("unchecked")
" q$ p  m& U% ]9 |8 a1 c    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
0 f  j/ m8 j9 n1 O; @9 b        final ValueOperations<String, T> operation = redisTemplate.opsForValue();- k  b0 {9 k8 Y' [6 Q2 M
        operation.set(key, value);2 S% \8 Y5 H# w& r6 T1 r5 {& d2 M
        return operation;. T) a( K; j; k" q( Q+ [) n$ ^
    }) q0 Y) y1 K; u0 q  |* ^5 s  k& e
) ]  C4 k2 Q- m' Q3 Y7 r
    /**8 B3 D$ E' T" z, R* V  ^; i
     * 设置对象及失效时间 (单位:秒)" Q8 `: L' y# A$ g+ L1 V# u
     *) k9 h5 D$ i+ H# C
     * @param key key; P( Y. o" I1 j: k2 q
     * @param value value值
2 Q4 m% p6 L6 f  c     * @param <T> 返回值泛型
( T- R* i4 E  y     * @param time 秒值
! d" e" t6 w+ @     * @return 正确的值:<T> 错误的值:null
- M" W. ]5 L' V( n5 E     */' i2 c' c, i7 X; I( \
    @SuppressWarnings("unchecked")
4 _1 Y" a! _( g7 b. r    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {2 V. N; S0 Y. e) ?" O) Y
        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
  k- I0 G( O+ Z  F5 k7 y        operation.set(key, value, time, TimeUnit.SECONDS);9 b0 J. _4 d8 A0 y/ L' J) C3 \
        return operation;3 S& ?/ T$ R- U$ j( f% g  I8 o
    }
% B3 G- H4 L9 w5 w  L2 h3 j! b8 b. o  e( h# N- z9 I' L5 j' C/ \

' r! q- U& y8 J5 |* f    /*** I, ~( p' O* H- r" [
     * 设置对象及失效时间(单位:毫秒)
' E- G( l5 [' Z+ Q; p     *
/ Z# x! D* ~7 p/ i4 V     * @param key key
; j( Q: v1 T9 _1 W     * @param value value值
1 f' K! _: {' [  V+ J  e     * @param <T> 返回值泛型
3 s, T! {5 n' @. v8 {     * @param time 秒值4 x  u. `" g" q% t& F% g" s
     * @return 正确的值:<T> 错误的值:null  m9 m+ f/ ^6 j
     */$ C- h! K( S5 o2 r- J; c
    @SuppressWarnings("unchecked")
1 j5 A" ~# v& a# R    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {2 u- g% F# E0 S; g5 X0 c; _
        final ValueOperations<String, T> operation = redisTemplate.opsForValue();: v" P7 }5 C+ T) i; }6 C$ e1 v
        operation.set(key, value, time, TimeUnit.MILLISECONDS);
0 i+ a( H0 a/ x        return operation;
+ h0 Q' S: {2 ~* h& l    }
4 u5 U2 e0 q7 T
0 w$ B" ^. j4 |5 F2 h    /**- p5 [1 _( A. T6 K
     * 获取对象0 X) k$ n" P* t' E1 ^% x  ?# `* n' ]
     ** g* u$ D  z2 c* S( o, u
     * @param key 键
/ l! g4 A0 V5 p3 {5 _     * @return 正确的值:Object值对象<br>
2 v) j" Y$ e- s: r  @     * 错误的值:null
' x# V+ N  K3 B' ~. h; Z3 Z     */& V5 p4 l- s3 v2 l. A. z
    @SuppressWarnings("unchecked"). M- x6 D+ @$ v- @
    public Object getObject(final String key) {: F4 D6 T& Q$ v, s
        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();- @  ]9 b) d8 m+ c
        if (valueOperations == null || !redisTemplate.hasKey(key)) {6 l2 j( i" B2 m; z+ c$ S" q
            return null;
- R% w: }% s0 q# S        }
5 |4 e! [7 p+ g  z# V7 g        final Object object = valueOperations.get(key);& s, t4 K4 i* y' N) ~5 P
        return object;% n8 z  }) W# e! N
    }. O0 ~9 B6 U( \  l, t1 m& H

5 X. d( X6 g! n! T! m. u! k    /**% X* I0 k" ?" I/ X0 v) p1 q
     * 从缓存中获取string值, o- @( w: h$ t3 B
     *: N) c  {- k4 _
     * @param key  _. i7 ]/ @! f$ h" r/ r
     * @return*/
4 O0 n' Q  v, {1 u( G    @SuppressWarnings("unchecked")- S- V8 [/ ^0 w4 X. X) l
    public String getString(final String key) {
7 f2 d/ z% q1 n$ M7 D( C        String value = "";% z3 w$ ]5 Z, g* t/ h
        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
$ C& y' H' o( ~: l0 ?# W        if (valueOperations != null && redisTemplate.hasKey(key)) {
6 G- f- k- d$ ?5 ]' H- h            final Object object = valueOperations.get(key);* R( ^$ M. Q: p/ @: _5 z& Z6 j" ]  g
            if (null != object) {" t- ~& @: S6 D8 f+ G2 ~$ ~; ]8 U
                LOGGER.info("--getString--object not empty");
+ p+ E1 p# E5 x  x! W' W                value = object.toString();  b" U# L) n; S5 {+ }9 Q) q
            } else {
* J2 x  s4 W% K                LOGGER.info("--getString--object empty");
9 r- G+ e8 }% o/ |1 `            }  n% K& M( J: a. k
        }
/ S, q* ]% y* Q8 e        return value;: T9 Z& X& p7 m! S1 L  P1 V) y' |
    }
/ ]  A+ K0 N( y  y5 c$ N0 {! f# c2.2:在redis中实现时间控制& z1 N$ O- u, D; ?" @, f4 \* O
9 I' V4 ~& C* G; S2 q
2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。7 f% V2 @. A; Q+ I& e0 Y
% F' f2 T6 u! i. x/ P  c+ w
import com.youjia.orders.redis.RedisManager;2 F7 [' [1 H% O1 F; h
import org.junit.Test;7 Q+ e* l: r$ ?) M- ]5 x! L
import org.springframework.beans.factory.annotation.Autowired;
9 b7 _1 a1 d4 E$ ?/ N( L, C2 A, m. X+ i6 E$ P
import java.util.Objects;
0 S- q+ e$ V1 v. [7 m4 o! [2 ^( b' Z
/**% m( b4 y% N) A3 b2 l" h6 t" @
* @Auther: Yrion) h& E4 \1 [. ^. K& F' k
* @Date: 2019-01-11 23:36
5 I- K4 z7 G+ t  S+ z( g */# v$ C$ D7 ?% W( g. q% i
$ P$ o4 {  S4 D# A
public class RedisTest extends OrderProviderApplicationTests {6 Y, u$ [6 `. h1 F3 L( P
  _/ v( G8 O9 F6 @" \
    @Autowired
, u6 U, n  {# E7 f% S, ~0 c    private RedisManager redisManager;$ z1 `8 W: Q  `
, l0 O2 F. @5 [& B$ l- h4 r* _
    @Test: a% }( `3 o+ |, U) a2 \
    public void test() {
- V5 J9 ?+ X; C# g$ s; ^) H7 {: K        controlTime("10000001", 10L);
* d# \# P! b( m9 E$ F, b4 q# v    }. m! D/ a9 N4 u/ W( X

" a% @; b2 p; I    public void controlTime(String requestId, Long timeOut) {
* ]9 j. I; E' h4 j( a. X5 {! A" u( f5 d/ z- Q, W' ^3 M2 Q& {# z
        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
! h; H& N3 w5 u) R9 ~            return;% t2 R3 d- U8 [! H5 j/ u
        }7 L2 L& O1 K; l# |8 b- T5 i
        //something code8 q7 J. ]8 z# E  X
        final String value = "value";9 l: A- Y; D6 x. P' e) x/ e+ Z) m
        redisManager.setObject(requestId, value, timeOut);- ~$ Y2 m+ g8 d: r
        final long startTime = System.currentTimeMillis();8 U: Q, e' k; N9 l0 y2 f
        System.out.println("开始控制时间");
6 n# T* y/ S5 e" r        //start) d5 \9 q1 c; y* R! ^
        for (; ; ) {3 U: V5 f7 w: r$ F, i
            if (Objects.isNull(redisManager.getObject(requestId))) {
/ P0 ^5 R, |0 M& w                break;* z' Y' Y9 S: ?  I  H4 R3 I, J
            }
- o# E7 V; \! w        }
; p# l0 j  h1 s' W& R  g$ a( ?        final long endTime = System.currentTimeMillis();
$ D, ?3 A! r! W- D1 y. E
* {" o# K  s5 O* G. Y        final long useTime = endTime - startTime;" j9 i8 A1 q  M2 `7 l( O, `

9 z- T% q9 R$ Q3 S        System.out.println("一共耗费时间:" + useTime);
0 x6 c9 I: Q8 x- J    }
) A# o$ B8 u" \/ I# F) @  `! C}
$ G. }' |- L- _0 F1 \outPut:
$ g1 H) _; H; }4 J5 F' r2 l* m: g$ Z6 o; R' x
开始控制时间
" {/ H1 P% K4 J. l" u9 Z0 a一共耗费时间:10042
8 l1 n( Y* G1 x1 C  [4 i1 E三:总结& a% L! _! x% J8 }+ U  B
' I5 \# e1 _* |# q9 w1 U
本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!- j3 D# ?8 L0 G
————————————————; t* p/ D  V8 x8 W! i+ z9 D
版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 i9 C# _% z5 y- D7 w4 Y原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933255 |1 t9 S5 ~0 i0 T! d
2 b; Y/ V# L2 k8 z6 }, X
6 t" \7 Y* v1 S3 M6 R4 f% C8 ?

作者: 1661888967    时间: 2020-5-6 09:57
学习一下,感谢分享
7 I" G' c  b; k* |- b: ]




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