QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1501|回复: 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

    # A' s) u8 t5 U9 [4 }Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    . f; R3 h- `% b1 {+ ]7 z
    1 M& v9 v7 x3 @% ]" F! q3 t# J' Z一:时间控制的几种方案
    ! ?8 m6 x  j! f* q
    / \# R3 ~! m4 K8 H0 m) Q$ D2 a3 E1.1: 从线程方面解决& g8 F) D4 U! B

    4 a% ~3 T1 l1 b0 L4 X最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    9 F% S2 P) k& V/ e" l; t; F7 E1 k! ^2 h. u$ ?; P
    1.2:使用Timer2 }7 v) H$ T" v1 V

    : C' N( L* q) |. c1 |: ~& k查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:% u! K2 {' ?7 Y8 ^4 A: q: [2 h
    - l& c9 ?( e0 {% ?
    public class TimmerTest {& n4 w$ t+ ^3 e1 q% ^: @
       /**
    4 G, E- }) n6 }  \' e7 }% X, P0 n) L     * 测试方法* B7 i% v/ E% ~6 \9 C, _1 V/ ~( M
         */
      U" T  T/ p# C" J4 K* F6 X% L    public void test() {
    & L/ H" y& L; f& f0 M0 z% r: ?: B- {. u        Timer timer = new Timer();5 Z( M/ I7 s7 ?+ r: d
            timer.schedule(new MyTask(), 800);7 }5 x7 ?6 T4 B; Z" C5 A1 r0 p
        }  S' x5 u7 X, S3 E0 b

      ]$ ]( b+ ?0 e/ O" T    public class MyTask extends TimerTask {2 @6 z# ~5 k& Q

    * c7 q9 [* l* `; c* G        /**1 G0 ~% ~0 H# g% S- X# M
             * 运行方法
    - x" H( K- x% D+ d         */; C& n! i/ t. h/ L; @: H! h/ e
            @Override9 `' Y+ r* g1 h( B
            public void run() {
    * V8 h) p8 q! N; H& u' ]  P& I            System.out.println("输出");$ B' k) Z6 w% j# y! k' r
            }/ p( y7 I( w5 O
        }
    * R8 H: Y! p' O( {3 \9 Y}
      L) P5 |0 Z7 b这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。: B: a4 ]9 n! y0 x/ }. J

    % y3 q) u* ^2 _1.3:redis延时
    7 S+ C$ X: k+ |9 Z8 V7 G
      g8 ?3 R2 W. {: Q2 k7 i! ^在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    ) \& A/ C) w0 x# l8 I1 Q) L0 n7 a* W$ a. ]

    & \/ b4 y" D6 Q
    0 W- |; U& T# D通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:7 U, X' ?, u6 f, v" E

    8 f& N6 T  D/ g  Z6 i: N/ q1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    ! T, b# P: G: T8 `* N) @, [% l3 i6 e. _% ^
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    6 z6 [6 a+ b2 S$ Z/ G3 s1 s# h/ l% ~4 z( h, c1 C
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    6 {8 Y$ i6 T2 F. J( P2 F
    & K, g! d1 X( Q1 V二:redis
    5 ~. O$ p5 D. s8 B# F0 f  {6 H; Y2 a& u
    2.1:maven中引入redis/ x1 t2 V, Z& y$ W
    ; L. P% [/ c& l* v
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    ; W$ N1 M, X: A; p2 Z$ ~
    # I  {& e# T2 \1 {1 @<dependency>
    + p& u7 E4 D. ~* x% J8 Y5 L) ^; x' P  <groupId>org.springframework.boot</groupId>
    4 w( E5 H  ?  Q% e3 F+ _: f    <artifactId>spring-boot-starter-data-redis</artifactId>
    2 @" o% b& ?, G9 Q3 `      <exclusions>6 H" |; v7 @% e: @) i0 w* P& N* Z
            <exclusion>, `/ r" P, b, {, K# t' n7 U
              <groupId>io.lettuce</groupId>4 ?! R% g: E2 p8 k( O% x( N8 H
              <artifactId>lettuce-core</artifactId>
    7 J2 J/ W7 s* o7 R        </exclusion>
    1 W3 L; ~# Y1 L" W9 V      </exclusions>
    4 ?5 @6 M( n; _( ?$ P) ^1 U/ i3 m</dependency>3 o; A! V0 M1 e8 x
    <dependency>
    3 H; r# K0 b2 `9 U4 B! c  <groupId>redis.clients</groupId>% H+ V, k5 ]; k) c, T( A' m
      <artifactId>jedis</artifactId>
    * N- a6 k1 V7 s</dependency>
    1 O8 l# Q/ t$ J, T! A' A! v2.2: 在springboot中配置redis
    3 c0 O" g3 k6 i3 _% v
    2 L: @0 y7 d" r- W4 wimport org.springframework.beans.factory.annotation.Autowired;
    " o1 I% C) R8 q2 l  D9 Simport org.springframework.context.annotation.Bean;$ V4 U0 X, P( j, F
    import org.springframework.context.annotation.Configuration;
    ' j2 Z& W$ b2 O9 K2 U2 u, F6 Dimport org.springframework.data.redis.core.RedisTemplate;+ h# ~7 `$ G7 q1 Q1 h
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;. F$ O; b8 b9 g- c& E+ l5 d( E% R6 ~3 |
    import org.springframework.data.redis.serializer.StringRedisSerializer;3 a4 m; w( P0 v, U5 |2 d8 Q* ?

    7 F) ^3 F9 B* H6 ]@Configuration4 J8 ]8 a+ U9 ?
    public class RedisConfig {7 h0 i$ {; g/ }9 u
    ' M% `/ w9 p# d0 n
        @Autowired' T, e" d: K) R1 k$ E+ C* y
        private RedisTemplate redisTemplate;$ m; M; L; S& C

    , e  c$ z! I) i7 w" d    /**
    2 X; p( a% _" a1 ^     * redisTemplate实例化
    4 w. S- C+ Y' l     *# C# }5 Y$ x3 D# J( a
         * @return
    , M' n, O& a3 S+ f* E/ Y! [     */5 c1 x( \: N* C. b" B# j1 C* n
        @Bean/ J$ u2 M4 x' V" l
        public RedisTemplate redisTemplateInit() {
    4 @2 g+ W, {- p* A) ~) ~. o, Y        //设置序列化Key的实例化对象$ G. ]% d1 Z* s2 Y
            redisTemplate.setKeySerializer(new StringRedisSerializer());1 G2 `2 N. _4 J
            //设置序列化Value的实例化对象
    # H. `3 t' |4 I        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    ! w6 e- q) [5 h; s5 n        return redisTemplate;
      ?* I$ a. y) g    }
    % ?+ _; v( D8 O/ d. D
    $ V, r1 [, Z( {3 w0 `7 C! {}
    * Q5 `5 t' {& @6 z. a; d2.2:redisTemplate模板工具类' d- J. F- ~5 _3 z  q

    / H  X9 ~3 _4 _" V4 R' t@Component' T, u( y  v8 s
    public class RedisManager {0 W$ c$ a; W. M4 S1 c+ L5 t1 c+ w
    , v) y( B1 S* s) E9 m+ F3 w# `
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    4 d# }3 A) E+ R7 P3 m$ G6 ]% h4 T: T
        @Autowired
    ' x( _- u! \# [% I, \5 w7 A    private RedisTemplate redisTemplate;
    / p; ~/ x# M; `- i4 n% m' J" @0 [' D) G
        /**
    3 @, Z. M5 z5 p* h' b+ K$ A     * 设置对象
    8 J. v) K; i; r     *
    5 ~# T8 i1 [8 g* V/ V5 M     * @param key key/ ~  a& Q7 A- a1 P6 W5 @
         * @param value value值
    $ N) b! i- i4 `+ ^1 U# u& A1 U     * @param <T> 返回值泛型+ J2 y; \8 Q' Q# N+ v6 P8 k# [1 @, u# \
         * @return 正确的值:<T> 错误的值:null3 H! Q8 T, N- R- @/ u
         */
    . r7 u; }# \5 P  U    @SuppressWarnings("unchecked")
    3 h$ n5 m) `+ m- `2 D4 I, Q    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    5 P+ X& U$ I; f; ^  h        final ValueOperations<String, T> operation = redisTemplate.opsForValue();- X! \. p( f! p4 G. t4 d0 W" V
            operation.set(key, value);
    ! Q8 Z. B6 u% j2 r; v        return operation;
    % t; c. G) D" r5 J5 [% W, L. M    }
    4 _, I* s# x! P: j$ [$ w2 o7 f
    6 O0 C  C5 V/ {    /**% T5 z5 ?2 R' U/ n" D
         * 设置对象及失效时间 (单位:秒). Y2 D$ p. l: ?* z4 A
         *
    5 t& j* i6 y" g* x7 E+ h     * @param key key8 D8 y; ], a& B: ~
         * @param value value值
    ) A% D) U8 Y: w6 x1 s     * @param <T> 返回值泛型
    5 s# X" q* @; V5 |# |     * @param time 秒值7 |7 I/ r) E% x' C8 t
         * @return 正确的值:<T> 错误的值:null
    6 ~/ U! ~& k/ K/ {! \     */. F- G/ k8 o3 f  [+ z9 p
        @SuppressWarnings("unchecked")
    - x* h7 z' c" d3 {7 f5 G$ r    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {+ ?0 U5 F- k- \# y
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();2 P5 U* _" a! E1 h; V
            operation.set(key, value, time, TimeUnit.SECONDS);
    , m$ M5 R: H0 U! O2 E        return operation;
    3 q4 h+ r3 J4 X' I1 }8 l6 L    }
    ( _, R" |$ f* y
    3 {) y( F( D; o; b: y& }
    - P8 V  K: F& K; O! Z0 P9 c2 Q- v    /**
    ' ~/ M7 B. \% D     * 设置对象及失效时间(单位:毫秒)4 i$ W# h/ A6 {, v# R" w0 `
         *
    . `# B& ]- X4 k" J- L( \     * @param key key
    * g% I/ H3 d7 O1 f     * @param value value值
    ( s* U0 r9 V9 @7 [$ h3 p" C& c     * @param <T> 返回值泛型4 _7 B$ e  y1 W0 J0 t
         * @param time 秒值
    $ M! d8 P, ]4 d" L# K' O     * @return 正确的值:<T> 错误的值:null
    4 F* E/ e* a7 c! W     */5 m: U( a1 n/ T% \6 w5 U
        @SuppressWarnings("unchecked")( k3 ]+ x, k. x  s9 h- R; Q
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {3 T; P$ C( j# d) |( N
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    0 X/ U2 y# z$ o% q8 Z2 E        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    % Q/ A8 W( [& F9 K7 a2 c        return operation;, u$ v$ n% P4 p- @8 ~& e
        }
    ) T* V9 _' o& g* V- x4 b4 N; p0 j9 q% H
        /**$ Z2 W0 B9 L3 i/ _: F! s3 i
         * 获取对象/ {9 d: a8 S9 ^" J
         *% x+ @; h. h0 h
         * @param key 键
    ! J/ h' A: |6 J( M* M     * @return 正确的值:Object值对象<br>; w4 c% a( g, O! y/ l2 `- M! P
         * 错误的值:null0 W$ ]% v& ^1 Q
         *// [% ~9 l" t: ^
        @SuppressWarnings("unchecked")( s! Z2 J' M# u. O* l4 ^
        public Object getObject(final String key) {
    * C" w5 A* W' u        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    " S, P! p+ [! t5 e& ~( s        if (valueOperations == null || !redisTemplate.hasKey(key)) {7 n; {, p% v1 ~1 J" D# [
                return null;
    7 l4 r/ D/ |: b0 N: ^) J! x        }0 g7 P7 M) O% x3 f7 Y' N+ C9 q9 q
            final Object object = valueOperations.get(key);
    ) Z5 E' c) g. c' g$ S% p: l        return object;
    2 p, ]: w" v5 `4 `2 E& g; R+ E    }' Y7 `: N. m+ k( D

    3 D; F1 v) b- e/ c' t2 n/ A    /**
    4 {+ G# y- ^  t+ L# \/ |6 @; L     * 从缓存中获取string值
    2 V# c% K9 \' h& }. v1 O( Z- \     *
    . x' c4 u( r5 w: M     * @param key
    $ ]( b0 u1 C1 J. H     * @return*/
    & P7 e" |5 j. A7 s    @SuppressWarnings("unchecked")0 D( K9 a% q' D+ @
        public String getString(final String key) {% ]) D/ V3 F/ T" _5 m: _/ P
            String value = "";5 c" `1 B8 N. n/ O1 [" [# ^
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    # L/ c$ x; c" n, F; Q  G( u4 O        if (valueOperations != null && redisTemplate.hasKey(key)) {
    ) S6 E) k0 x# h9 L0 Z" R' k# G4 [            final Object object = valueOperations.get(key);
    8 w5 g2 [" x6 V+ y/ n  d            if (null != object) {
    # Y' Y6 N$ B6 I                LOGGER.info("--getString--object not empty");
      h! T- M2 p, b9 e# S- N                value = object.toString();' S& z6 a6 I& F  }, B' L  `
                } else {
    - E2 S$ Q; P, }+ y, ]& z& W                LOGGER.info("--getString--object empty");$ g& [* c6 i; F2 x8 h' ?
                }
    1 _' q1 ]$ z8 j7 O        }: D; r. w# u& V- }0 z5 E% _
            return value;
    ; Z, J  A" R" ?    }1 A$ o$ F. f8 j: Z8 x* h
    2.2:在redis中实现时间控制
    8 z$ z" m0 {% a7 M: v/ I: U" A; H6 L& k6 R
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。, J  U  u* P5 j3 M  }
    4 V8 `, a( x0 i
    import com.youjia.orders.redis.RedisManager;
    2 C) @* b# ~& o. h! n$ Z5 Kimport org.junit.Test;; [4 z8 o1 ]' W8 x, x, \% J
    import org.springframework.beans.factory.annotation.Autowired;- D1 Q8 V& L2 x/ H$ I( @

    4 o7 Z+ `+ r1 w+ @import java.util.Objects;( O+ R! f% M3 k

    + R$ I/ a. v# ~: G0 o0 E9 x! f/**
    ) n2 L1 Y* o8 t; g8 k+ R * @Auther: Yrion9 w% K! j9 N+ K1 b0 n  v& b, h
    * @Date: 2019-01-11 23:36# O" |7 ?8 _5 Y) \
    */
    " ^$ H( o, Q: }1 a- |9 X+ O
    4 Z: b9 J$ f; R. \public class RedisTest extends OrderProviderApplicationTests {! j  t! R' s. e( k
    5 X7 j  K9 A) b$ ]  }' A( O) Y) z
        @Autowired+ [) A6 z: X1 [' k! R; s
        private RedisManager redisManager;
    . n2 Q7 ?6 `8 w  Q$ a
    ( n; a- t9 @2 A- P5 O    @Test; Y' f+ A, x: O3 L$ N) m* G
        public void test() {
    ( `. n% J5 b, V( Z$ K( l        controlTime("10000001", 10L);- U; @+ f# j! G6 [( E) {
        }
    # \$ L7 m9 M* s7 c% M! p: p
    % D1 K! O9 O7 E6 D* m    public void controlTime(String requestId, Long timeOut) {
    4 W# o2 S' Q/ ~' s& q+ x1 Y- r& ^. V8 p( z% G1 ^( Z/ K
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {/ Q! m( R' x* \0 y; X3 e- D
                return;
    ' R, r/ `+ f" f1 i! Q        }
    . Y  ]- G& r# z        //something code5 W# U7 ]/ T4 ]% C8 Z
            final String value = "value";+ T9 W/ m" [9 C! ^* h- J2 x
            redisManager.setObject(requestId, value, timeOut);
    ) w$ B! t" }& u' \# P0 ]$ J" K        final long startTime = System.currentTimeMillis();
    ; Y/ V; m# `( l) ^& s8 E        System.out.println("开始控制时间");
    % N6 \; L! j4 f0 o5 l7 Q0 |        //start4 g: b9 _$ ]4 X7 y) \& N& \2 }
            for (; ; ) {
    8 x/ U/ ^5 l. W            if (Objects.isNull(redisManager.getObject(requestId))) {8 C7 g2 w4 z' N- W' w  b
                    break;
    ! G- T# W# |2 J: {            }9 _: r. e) T# i% p
            }
    ' u/ f3 b1 D2 n, ~: |& g        final long endTime = System.currentTimeMillis();
    ! B: A6 I9 m! V) a) r6 |* g" S+ F* o) B
            final long useTime = endTime - startTime;
    & E$ N8 H+ K4 F' l7 x. l/ x, y$ T
    / g$ B( s/ X9 A, [/ E/ P        System.out.println("一共耗费时间:" + useTime);& a9 d) s$ ?" F5 w- N- Y
        }
    6 u: k" Z0 D% Z7 j; Q  c}6 a& b: Y; }3 M
    outPut:8 Z+ b3 Y3 K" L
    2 T7 O3 W' T5 q" @) F7 F
    开始控制时间% U! R' y& H/ d$ s( ?. T! J8 a2 e
    一共耗费时间:10042
    8 e4 r- Q7 _( R三:总结1 A/ l4 ~0 A- h4 A2 d1 n; F

    % ^8 t, c7 s* Y% e$ Q; f; I本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    8 [: l' N, S5 m: D& J/ M: _: h————————————————  C1 r- ]: ^+ i* D: `- r
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 E3 `/ g$ H9 Q6 M) J5 O
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
    ; Z8 @0 `  R: v& a
    , W8 c, s) o- y) r3 P! M$ g( P
    ( P/ J4 i8 n& J- V
    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, 2025-8-24 03:44 , Processed in 2.180848 second(s), 56 queries .

    回顶部