QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1591|回复: 1
打印 上一主题 下一主题

Java如何优雅的实现时间控制

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2020-5-3 16:03 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta

    5 t  R+ \, }' I* {* GJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。8 u1 d0 `  {$ Y- x3 g: a1 X' y8 J4 `

    6 y4 F/ h( m3 y4 t/ G! ^7 r, b一:时间控制的几种方案3 n* }, X$ N- S3 l

    6 `1 q/ b# m, u5 Q1.1: 从线程方面解决
    3 F/ t1 m+ f6 t& x* l: r1 i  T; L5 X4 x( X% L- Z
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。! c. R) Y8 Q) F2 O
    . A* w% [0 U2 b, D
    1.2:使用Timer( ~$ B. j6 a, A' z) ?4 \3 z

    . s7 t9 c' t* L& M3 B/ @- j4 U查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:) [8 D/ w5 o; z; w% W

    # w; e( s# ^8 D/ x; s; kpublic class TimmerTest {  |) I5 i3 H# c" R
       /**  G4 I, t/ o9 s% _# X. q, ^
         * 测试方法# j) q0 ]5 E$ R2 ?
         */
    ) B0 `  T' g# |' I" Q    public void test() {) G1 x6 c; S% y( b3 C' f& e( [
            Timer timer = new Timer();
    $ K' Z/ R/ d1 j! J/ ~2 w' J3 j# b        timer.schedule(new MyTask(), 800);) i( m4 O: u, S( Q4 C5 B  j
        }
    7 Q; ?( a# Y* t; w! `- Y( R. U( ^8 F2 H- o4 f
        public class MyTask extends TimerTask {
    $ {+ D# i* c7 {6 Z, H: }9 z% j
    # i$ p' \5 i: F& j8 N9 I  }' a        /**# E0 |# U0 e7 d8 F4 L1 h4 M
             * 运行方法
    $ }$ W1 A- D, x8 @6 j" @& o% m         */
    ; M' h2 V3 p4 P6 G* N( \        @Override/ k" \% M) X+ A# E$ W! o8 B0 B0 A
            public void run() {
    4 S. ^! ^0 N& _            System.out.println("输出");
    # \# J+ |7 r5 Z8 P4 m1 Q        }
    * g5 ?, P# z+ t, _: F    }
    2 z. S" N; A  T( {! U}
    4 d) s. e# J( s; G8 ?4 w4 M这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    * e8 n, p) a# a7 W+ \  [6 p( r
    5 R: `5 s, Y2 u0 W$ F1.3:redis延时, q4 k4 ~5 Y; Y: [! h# X2 \6 x

    # K; g2 z( O1 e* w* x在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    ( {3 r5 ?" F6 r- [" j
    : q- [% f# S& H$ R! i
    7 h" M, w3 b$ T# Y+ I* `: |4 s! a3 c& W9 `  L
    通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:& B# L2 _) Q6 D; q( n
    0 b% E& g8 M- y: n2 A
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制( w1 C/ [7 O4 I# K% Q3 s  ~7 ~
    1 W, M  w" Z! r# S, w
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    : a/ ]) j+ T% M  m3 c1 @( I& N+ X4 g+ w) P# i
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。5 q9 S+ f, g/ [% d' A

    3 }& X. T1 a$ l+ ]6 _/ T6 O& R二:redis6 s) _, D5 t2 G) o7 H8 P0 g/ P

    ' I+ b0 t# R6 v) Z- {' K( J2.1:maven中引入redis7 Q& t8 f8 ]$ A# h. T  ~! Y' }$ I/ b
    : {& Q. H' Q7 ~3 _4 ^, g
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。6 S- i; [5 k! X+ K1 X

    + [) T# Z' K8 G9 C. ^6 L9 K<dependency>
      L( m& g/ M5 u! S  <groupId>org.springframework.boot</groupId>4 C% F* N& N+ Z; h) o3 O
        <artifactId>spring-boot-starter-data-redis</artifactId>* r" H( q& a1 m2 v5 b9 L% b
          <exclusions>; e% `$ E. _; j2 G1 e
            <exclusion>
    - ?  B* j6 c" q          <groupId>io.lettuce</groupId>  R: U; @6 Q0 ]
              <artifactId>lettuce-core</artifactId>7 j# K( B' d) D, f8 l
            </exclusion>* |7 o7 I+ i+ ~/ J1 s: e
          </exclusions>( Q6 h4 g/ f8 G4 i- w
    </dependency>
    0 ^. A; d; v, l<dependency>1 b& w9 ]+ j) W
      <groupId>redis.clients</groupId>
    * J/ A* L( c& Z. w  <artifactId>jedis</artifactId>5 V8 S/ T% T/ J. w# O3 w% p
    </dependency>, i  ~1 s$ Q9 M+ f2 i) q! q0 U9 E
    2.2: 在springboot中配置redis
    ' H, m* a  y. N
    ; ~0 e# d% [, g; N1 K" Wimport org.springframework.beans.factory.annotation.Autowired;
    ' [3 A; U) T1 B4 n. Yimport org.springframework.context.annotation.Bean;
    7 `2 W: ~& m+ e# B7 }' z7 Uimport org.springframework.context.annotation.Configuration;
    * ~4 \1 u7 f$ B0 L; nimport org.springframework.data.redis.core.RedisTemplate;
    ) g, B. W3 S2 z* _! e, l$ bimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    8 w; w/ C; j. n3 v0 H+ y6 Bimport org.springframework.data.redis.serializer.StringRedisSerializer;" M' X' [8 Y& {% `' _
    # W% I/ d% R5 R& ~" Z
    @Configuration
    1 G( ]7 A1 f0 u! _6 Cpublic class RedisConfig {
    & j, f8 U3 n7 J% b
    7 E( z3 s6 w1 {# Z$ Q    @Autowired! r( i# C* d' w" Y: n% K
        private RedisTemplate redisTemplate;
      }: a& P+ M. f# _  g0 @0 \( {! M
    ' g& D, e& C$ A    /**
    ! Z$ V% t  o* K+ V" a     * redisTemplate实例化
    " _4 C+ W* n; I# }1 r9 M) e     *
    / Q5 j& f. D; n! P     * @return) t$ W$ a' [. D9 |: I. P* m
         */( A1 y! K1 ]: {8 H$ E
        @Bean
    & V8 o# V. ~+ @3 b4 }    public RedisTemplate redisTemplateInit() {/ {2 B3 m! k: f8 _( P' w
            //设置序列化Key的实例化对象% w! Y' ]5 q& j0 ^% C5 h
            redisTemplate.setKeySerializer(new StringRedisSerializer());$ X- L1 ~0 \% U5 C9 Y5 }6 d$ ]
            //设置序列化Value的实例化对象
    0 y2 S+ L4 [# }  Q' O        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());+ ^9 g) d/ d2 S/ e
            return redisTemplate;" _) C( g; u1 W# S
        }2 i$ A1 ^* m! V% X  P- o+ _
    $ v+ }7 {" u& D4 k
    }, {8 W2 g  Z5 o' e) u3 y
    2.2:redisTemplate模板工具类4 {& A( G& }* k( S3 U) P
    5 |5 _# g* }, D0 D& ^* k' n1 h
    @Component; w0 k9 d, j& e2 `! y1 Y
    public class RedisManager {
    : j% z2 L/ W, K7 L! B1 t
      t7 U& u6 j* P! g9 Z( k, }. e    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);; B; u, h7 @7 ?, A1 [! `

    4 W( c8 q% w- V: V7 C: h    @Autowired
    1 I4 W4 K/ p' Y    private RedisTemplate redisTemplate;
    : v) h) E6 o. s# v/ W4 S3 V: ], d2 o4 N
        /**
    1 \2 Q4 y4 y) \     * 设置对象
    - d$ ]% x9 u) F: w     *  P# `$ Y7 i- Z& @" \
         * @param key key' ~+ d( I& }# I8 k( t
         * @param value value值
    ' g# L  F; v' M$ P     * @param <T> 返回值泛型! ~) M) ]& v8 \( z5 ~
         * @return 正确的值:<T> 错误的值:null
    0 z- e; |+ |/ T5 {" w     */
    + R$ s  ^( r) J1 i! Q$ G+ s    @SuppressWarnings("unchecked")
    & A. V1 \' J4 _9 T* T  ]! h    public <T> ValueOperations<String, T> setObject(final String key, final T value) {' j% d3 R. m1 v9 y/ s8 _
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    ( Q5 v& Y, N" e3 D' i# W- X        operation.set(key, value);
    " E% t: \) ]( Z: G; u        return operation;5 z) O( I0 f4 b" l
        }; O5 `  u1 `3 c- }5 X! {0 q

    ) r# @% Y' T; G: z    /**( K3 r& i% @# A& Y! m
         * 设置对象及失效时间 (单位:秒)* N0 X4 d: t# j5 K! \* H$ `
         *
    8 N/ K5 a$ G( m. K     * @param key key
    8 {, n4 Y( c& ?( v( ^' n5 O     * @param value value值
    1 x1 w! x: G4 {     * @param <T> 返回值泛型
    2 @4 q! t4 J$ C! |, Z     * @param time 秒值
    5 x( t% B, r; t1 a% M1 d     * @return 正确的值:<T> 错误的值:null
      D2 X; |- U  x, n& ?9 i' I     */
    4 p; P# F7 ]% h! Y' Y    @SuppressWarnings("unchecked")
    % n4 r. W& U; L    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    ; R4 [$ n9 m8 y: u2 O0 e        final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 O. W3 K# B- j' O8 Z5 Z
            operation.set(key, value, time, TimeUnit.SECONDS);% o' W7 J* `9 v5 M( a4 X' z$ o
            return operation;) ?4 d* ^% ^% e" q2 i, I! \0 K
        }
    . g+ B2 Q/ r3 s( y1 o& b% E; L, r2 L/ I- u8 y
    0 U8 n2 B) I% |6 k. l
        /**' L. J6 q; z6 Z7 t
         * 设置对象及失效时间(单位:毫秒); y9 g/ f& s/ u
         *5 F* S/ w  j; ~, w
         * @param key key
    9 {1 m8 z# I* K( q/ p     * @param value value值: B7 r$ j2 k1 m* b9 W! l# J  X
         * @param <T> 返回值泛型
    * e" y7 @& Q. U4 a     * @param time 秒值
    & q& }  L. ~; G6 l9 C: R2 e1 c6 Q     * @return 正确的值:<T> 错误的值:null
    ( s" I' [- O* w$ k, D     */
    + z1 ]) _" P3 ~+ u    @SuppressWarnings("unchecked")7 r8 @* m9 s' r% H; [6 Q
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    6 z0 b$ M" I, T* H, z0 V        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
      j5 G+ ?6 v/ @7 Z/ u& ~: v( n        operation.set(key, value, time, TimeUnit.MILLISECONDS);, i% V, c/ `. ]& e0 I% q
            return operation;
    + E0 y; z$ e  l& d# O9 E2 i    }. Q, q8 W8 j1 Y. h8 x7 @

    4 U9 E& L" p. j% E# J2 c* g    /**) i- w* `  K( S# H6 x+ a
         * 获取对象
    2 f; @" J7 |+ ]7 m4 {& a- Z) L/ ?. [' V     *
    ; H, a; H6 t) F# |( q     * @param key 键5 m, Y8 B4 q, C
         * @return 正确的值:Object值对象<br>- S) R: M  o: D7 D
         * 错误的值:null2 K+ N; I* R$ k7 n1 @( f7 V: m' v% i
         */
    5 c% \" ]( ]" T& t# z( y    @SuppressWarnings("unchecked")+ c* s& d9 t; M( O( ^
        public Object getObject(final String key) {- K1 n* n1 o$ J0 K) U0 D& H
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
      C. W! |* P, P4 k: }/ w  j        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    7 e4 T. ^* x( g, f            return null;5 l: i2 G3 H2 P& q6 V6 H
            }2 A4 `1 W9 f  x6 Z
            final Object object = valueOperations.get(key);0 @; k6 i5 f; e- o0 U6 f8 H! f' M
            return object;& ]7 q0 b& }5 p3 T
        }* `& T  V# M+ Y, i
    4 p9 M% p+ }6 x( u! Q% S8 z$ x
        /**
    . Y& W9 z, m5 T4 }     * 从缓存中获取string值# x" U9 m' }8 w, s, C; A# X
         *. S  o% \- g+ s- N$ u; L* b
         * @param key9 Q  \& Q; Q# X2 g% r
         * @return*/$ _1 v5 [, p) N- {& N0 c" B- s
        @SuppressWarnings("unchecked")0 k; I- H, P) y) C
        public String getString(final String key) {
    5 [& u: Q0 y# W" O        String value = "";
    * q/ I6 z( m4 _& O6 @- t3 P        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();% w/ a5 S9 T3 F. R
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    ' j3 E9 c5 r) @% g- s* s            final Object object = valueOperations.get(key);" C0 [% A1 R( H$ c5 w9 O0 h4 S
                if (null != object) {
    # Y1 p5 x: t; u                LOGGER.info("--getString--object not empty");$ T* j* O* l/ i) @5 [# J# O
                    value = object.toString();
    2 N, M; e# o. F! G0 [. z& V+ [            } else {) @! Y- B3 B& H, f# P: L1 E$ n# X3 ^
                    LOGGER.info("--getString--object empty");
    $ F/ N( g; a( `& m            }
    ' L* Q" J5 d3 [9 U7 g6 d; ~+ T& ]        }7 ?( ~' O. h0 w1 ]4 U9 {" }" S
            return value;
    . r7 P  S2 J- t5 \    }( l- s1 \& M! T3 |( v' P+ y
    2.2:在redis中实现时间控制. ~5 g3 a; F: `' ^% H  M; A/ }
    4 S4 ~: O9 V. v1 v3 n
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    1 h  L4 ]# w; o8 n
    , a/ A+ O" c- \' limport com.youjia.orders.redis.RedisManager;
      n1 |7 f; m; V6 m$ K: Eimport org.junit.Test;
    8 _5 g% s, {& r9 h8 W1 X# v( kimport org.springframework.beans.factory.annotation.Autowired;* p3 l% W- u: P& K' |% d1 e

    5 l' M  p) k" L2 s  X' mimport java.util.Objects;
    0 k5 r+ x' x7 E- h* l
    . z5 u+ B+ t% `9 l/**4 A! o* L+ z" v( N& R4 Q6 f
    * @Auther: Yrion$ h9 \& e5 v! T' j0 j) M- @) T
    * @Date: 2019-01-11 23:36
    ( _* {, N! S1 X7 Y  X */+ D# Y0 c4 N% L8 u8 e

    ) m' @, t) B; V- }( e- K/ Apublic class RedisTest extends OrderProviderApplicationTests {
    + G4 @* r8 P3 N: s0 }+ X, N% a/ W( c3 o/ V, e
        @Autowired% N# c6 }3 F) w# {* |* }
        private RedisManager redisManager;9 A0 S4 ~# N9 }9 b+ N. N

    # W3 q& Z* d* c% d, p2 s    @Test
    - \. y5 q. h# r$ ?/ o' C. O' F    public void test() {- p& v* u9 X; M* }  T8 M, k
            controlTime("10000001", 10L);0 J+ z  ?6 A" z9 Q9 {1 \
        }7 Y4 C/ R! V% Y$ j5 E8 w$ A/ U2 U% f
    % q6 H7 \# K' ]; |5 E; F9 w
        public void controlTime(String requestId, Long timeOut) {4 x  x  F, @& ?1 x( s, {8 L

    . n- o5 i; M% W1 `/ _! |1 _, P        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {: ?# \% x) F8 ?9 e% T2 h" E; G. K. @
                return;
    ) l- G' |" O* G2 _        }
    2 ]0 Y& [. }! Q5 ^6 x& I) y+ e6 n        //something code+ ], s- ?1 a6 D/ s! w
            final String value = "value";
    6 C7 L6 T' f  @& K  E        redisManager.setObject(requestId, value, timeOut);1 ~$ I. p' e$ E8 X: g  \2 J4 j7 x
            final long startTime = System.currentTimeMillis();
    ! O6 [+ ^# }- B7 }9 G        System.out.println("开始控制时间");- F7 W4 D8 F9 [: {5 X& y
            //start
    $ |% t. P* w5 ~0 Q5 z- T( z        for (; ; ) {
    . ^# ]: l8 ~; v4 V            if (Objects.isNull(redisManager.getObject(requestId))) {( S( o& K! q2 Y* `
                    break;
    " t$ a# d$ j8 t  _            }
    / l: |+ H2 \: s, q% K        }+ r5 t7 J, O% L$ M2 i$ W4 o3 {
            final long endTime = System.currentTimeMillis();
    - I: K, \# K9 v
    . X& _. j( T1 o9 J4 k        final long useTime = endTime - startTime;" t5 o& @0 V5 x' U' h* z
    * {- y( w: k9 G6 u) e- ~
            System.out.println("一共耗费时间:" + useTime);9 r. P1 i$ s3 V5 C1 W/ i
        }
    ( @- e. G' \$ n2 J}. H7 y" Z% i, B. N$ ]! x
    outPut:
    3 l2 i) s+ I! v4 T4 _# Q7 H* r
    - ~" }* F) B- X2 n' |开始控制时间
    $ ]" X$ b% ^& z+ O一共耗费时间:10042# n. y$ }% `, i
    三:总结9 L3 n) L% J; q& K9 U* ]

    / x8 x5 |+ k2 H5 }6 b, {; _" S本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
      E  e& L  ~# V  ]% s$ {! [+ A3 G————————————————
    " }. w% r4 N9 t) r版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    " N/ t6 B* |2 W  k! T: r原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325- y* N8 u1 H0 N+ w: k' @
    3 E$ j' ^7 y& x5 [: ~4 A
    " O% |- [. C: C! u0 b
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信

    0

    主题

    0

    听众

    27

    积分

    升级  23.16%

  • TA的每日心情
    慵懒
    2020-5-6 10:54
  • 签到天数: 1 天

    [LV.1]初来乍到

    自我介绍
    马上比赛了

    邮箱绑定达人

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-4-20 02:16 , Processed in 0.411255 second(s), 57 queries .

    回顶部